diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 764d242..0000000
--- a/.eslintrc
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-    "parserOptions": {
-        "ecmaVersion": 6,
-        "sourceType": "module"
-    },
-    "rules": {
-        "semi": 0
-    }
-}
diff --git a/.gitignore b/.gitignore
index 60831bd..e06eeb4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,24 +1,4 @@
-# Dependency directories
-node_modules/
-
-# Logs
-log
-logs
-*.log
-npm-debug.log*
-
-# IDE
-.idea
-
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
-# Files
-dist
-testing/
-package-lock.json
-yarn.lock
-examples/drag/package-lock.json
-examples/todomvc/package-lock.json
-.vscode/launch.json
-*.tgz
+dist
+node_modules
+package-lock.json
+test/.test
\ No newline at end of file
diff --git a/.npmignore b/.npmignore
deleted file mode 100644
index a9b1e38..0000000
--- a/.npmignore
+++ /dev/null
@@ -1,26 +0,0 @@
-# Dependency directories
-node_modules/
-
-# Logs
-log
-logs
-*.log
-npm-debug.log*
-
-# IDE
-.idea
-.vscode
-
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
-# Files
-src/
-test/
-examples/
-testing/
-.editorconfig
-.travis.yml
-Gruntfile.js
-.eslintrc
-yarn.lock
diff --git a/.travis.yml b/.travis.yml
index 0a6a232..bb5c162 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,13 +1,9 @@
-language: node_js
-node_js:
-    - '4'
-    - '6'
-    - '9'
-script:
-    - npm run build
-    - npm run test
-after_success:
-    - cat ./coverage/lcov.info|./node_modules/coveralls/bin/coveralls.js
-branches:
-    only:
-        - master
+language: node_js
+node_js:
+    - '10'
+script:
+    - npm run build
+    - npm run test
+branches:
+    only:
+        - master
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..0afcc15
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,27 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "node",
+            "request": "launch",
+            "name": "Debug",
+            "program": "${workspaceFolder}/${relativeFile}"
+        },
+        {
+            "type": "node",
+            "request": "launch",
+            "name": "AVA",
+            "program": "${workspaceFolder}/node_modules/ava/profile.js",
+            "args": ["${file}"],
+            "skipFiles": ["<node_internals>/**/*.js"]
+        },
+        {
+            "type": "node",
+            "request": "launch",
+            "name": "AVA specific",
+            "program": "${workspaceFolder}/node_modules/ava/profile.js",
+            "args": ["test/createNode.js"],
+            "skipFiles": ["<node_internals>/**/*.js"]
+        }
+    ]
+}
diff --git a/Gruntfile.js b/Gruntfile.js
deleted file mode 100644
index ad9e323..0000000
--- a/Gruntfile.js
+++ /dev/null
@@ -1,152 +0,0 @@
-module.exports = function(grunt) {
-    var dop = 'dop.js'
-    var dopnodejs = 'dop.nodejs.js'
-    var dopmin = 'dop.min.js'
-
-    // Project configuration.
-    grunt.initConfig({
-        pkg: grunt.file.readJSON('package.json'),
-
-        watch: {
-            scripts: {
-                files: ['src/**', 'package.json'],
-                tasks: ['default'],
-                options: {
-                    spawn: false
-                }
-            }
-        },
-
-        copy: {
-            main: {
-                src: 'node_modules/dop-transports/connect/websocket.js',
-                dest: 'src/env/browser/websocket.js'
-            }
-        },
-
-        concat: {
-            options: {
-                banner:
-                    '/*\n' +
-                    ' * dop@<%= pkg.version %>\n' +
-                    ' * www.distributedobjectprotocol.org\n' +
-                    ' * MIT License.\n' +
-                    ' */\n',
-                process: function(src, filepath) {
-                    return '\n//////////  ' + filepath + '\n' + src + '\n\n\n'
-                }
-            },
-            nodejs: {
-                src: [
-                    'src/dop.js',
-                    'src/env/nodejs/**', // needed here dop.core.listener.prototype = Object.create( dop.util.emitter.prototype );
-                    'src/util/*',
-                    'src/api/*',
-                    'src/core/**',
-                    'src/protocol/*',
-                    'src/node/*',
-                    'src/umd.js'
-                ],
-                dest: 'dist/' + dopnodejs
-            },
-            browser: {
-                src: [
-                    'src/dop.js',
-                    'src/env/browser/**', // needed here dop.core.listener.prototype = Object.create( dop.util.emitter.prototype );
-                    'src/util/*',
-                    'src/api/*',
-                    'src/core/**',
-                    'src/protocol/*',
-                    'src/node/*',
-                    'src/umd.js'
-                ],
-                dest: 'dist/' + dop
-            }
-        },
-
-        'string-replace': {
-            dist: {
-                files: {
-                    'dist/': ['dist/' + dop, 'dist/' + dopnodejs]
-                },
-                options: {
-                    replacements: [
-                        {
-                            pattern: /{{VERSION}}/gi,
-                            replacement: '<%= pkg.version %>'
-                        }
-                    ]
-                }
-            }
-        },
-
-        uglify: {
-            build: {
-                src: 'dist/' + dop,
-                dest: 'dist/' + dopmin
-            },
-            options: {
-                banner: '/* dop - (c) 2018 Josema Gonzalez - MIT Licensed */\n'
-            }
-        },
-
-        replace: {
-            dist: {
-                options: {
-                    patterns: [
-                        {
-                            match: /\.core\./g,
-                            replacement: '.c.'
-                        },
-                        {
-                            match: /\.util\./g,
-                            replacement: '.u.'
-                        },
-                        {
-                            match: /\.protocol\./g,
-                            replacement: '.p.'
-                        }
-                    ]
-                },
-                files: [
-                    {
-                        expand: true,
-                        flatten: true,
-                        src: 'dist/' + dopmin,
-                        dest: 'dist'
-                    }
-                ]
-            }
-        },
-
-        'optimize-js': {
-            options: {
-                sourceMap: false,
-                silent: false
-            },
-            dist: {
-                files: {
-                    'dist/dop.min.opt.js': 'dist/' + dopmin
-                }
-            }
-        }
-    })
-
-    grunt.loadNpmTasks('grunt-contrib-copy')
-    grunt.loadNpmTasks('grunt-contrib-concat')
-    grunt.loadNpmTasks('grunt-contrib-uglify')
-    grunt.loadNpmTasks('grunt-contrib-watch')
-    grunt.loadNpmTasks('grunt-replace')
-    grunt.loadNpmTasks('grunt-optimize-js')
-    grunt.loadNpmTasks('grunt-string-replace')
-
-    var tasks = [
-        'copy',
-        'concat:nodejs',
-        'concat:browser',
-        'string-replace',
-        'uglify' /*'replace', 'optimize-js'*/
-    ]
-    if (grunt.option('build') === undefined) tasks.push('watch')
-    grunt.registerTask('default', tasks)
-}
diff --git a/README.md b/README.md
index 1d4dbe6..bea6e63 100644
--- a/README.md
+++ b/README.md
@@ -1,50 +1,93 @@
+<div align="center">
 
-<p align="center"><a href="https://distributedobjectprotocol.org"><img width="200"src="https://distributedobjectprotocol.org/img/logo.svg"></a></p>
+<p align="center"><a href="https://distributedobjectprotocol.org"><img width="200" src="https://distributedobjectprotocol.org/img/logo.svg"></a></p>
 
-<p align="center">
-    <a href="https://www.npmjs.com/package/dop"><img alt="npm version" src="https://img.shields.io/npm/v/dop.svg"></a>
-    <a href="https://travis-ci.org/DistributedObjectProtocol/dop"><img alt="Build Status" src="https://api.travis-ci.org/DistributedObjectProtocol/dop.svg?branch=master"></a>
-    <a href="https://www.npmjs.com/package/dop"><img alt="license" src="https://img.shields.io/npm/l/dop.svg"></a>
-    <a href="https://spectrum.chat/dop"><img alt="Join the community" src="https://withspectrum.github.io/badge/badge.svg"></a>
-</p>
+[![npm](https://img.shields.io/npm/v/dop?style=flat-square)](https://www.npmjs.com/package/dop)
+[![npm bundle size](https://img.shields.io/bundlephobia/minzip/dop?style=flat-square)](https://bundlephobia.com/result?p=dop)
+<!--[![Build Status](https://api.travis-ci.org/DistributedObjectProtocol/dop.svg?branch=master&style=flat-square)](https://travis-ci.org/DistributedObjectProtocol/dop)
+[![Join the community](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/dop)-->
 
-## Distributed Object Protocol is for
+<br/>
+<a href="https://distributedobjectprotocol.org/guide/javascript">https://distributedobjectprotocol.org/</a>
+<br/>
+<br/>
 
-**State management**, Remote procedure calls, Reactive programming, 
-Data sync, Pub/Sub, Optimistic updates, Time-travel debugging, Unidirectional data flow and **Real time apps**.
+</div>
 
-This repository is the JavaScript implementation of the protocol that runs on node.js and Browsers.
+**Distributed Object Protocol** is a thin layer on top of your data network that helps you communicate server and clients (nodes) using [RPCs](https://en.wikipedia.org/wiki/Remote_procedure_call). It is also a pattern that makes easy update, mutate or even sync the state of your App using [Patches](https://github.com/DistributedObjectProtocol/protocol#Patches).
 
-<!--
-## Connecting two nodes
+## Quick example using RPCs with WebSockets
 
 ```js
-// Server (node.js)
-const dop = require('dop')
-const object = dop.register({
-    fullname: 'John Doe',
-    square: number => number * number
+// Server
+const { createNode } = require('dop')
+const WebSocket = require('ws')
+const wss = new WebSocket.Server({ port: 8080 })
+
+const sum = (a, b) => a + b
+const multiply = (a, b) => a * b
+const getCalculator = () => ({ sum, multiply })
+
+wss.on('connection', ws => {
+    const client = createNode()
+    client.open(ws.send.bind(ws), getCalculator)
+    ws.on('message', client.message)
 })
-dop.listen() // WebSockets on port 4444 (https://github.com/websockets/ws)
-dop.onSubscribe(() => object)
 ```
 
 ```js
-// Client (browser)
-import dop from 'dop'
-const server = dop.connect() // Native WebSockets 'ws://localhost:4444'
-const objectFromServer = await server.subscribe()
-console.log(objectFromServer.fullname) // > "John Doe"
-console.log(await objectFromServer.square(5)) // > 25
-```-->
-
-
-
+// Client
+const ws = new WebSocket('ws://localhost:8080')
+const server = createNode()
+ws.on('open', async () => {
+    const getCalculator = server.open(ws.send.bind(ws))
+    const { sum, multiply } = await getCalculator()
+    const result1 = await sum(5, 5)
+    const result2 = await multiply(3, 3)
+    console.log(result1, result2) // 10, 9
+})
+ws.on('message', server.message)
+```
 
+## Quick example using Stores and Patches
 
-Check the website for more detailed information [https://distributedobjectprotocol.org/](https://distributedobjectprotocol.org/)
+```js
+// Server
+const { createStore } = require('dop')
+
+const store = createStore({ players: 0 })
+
+function subscribeToServerStore(listener) {
+    // Incrementing number of player as a patch
+    const listeners = store.applyPatch((state) => {
+        state.players += 1
+    })
+    // We emit the patch to all the subscribers
+    listeners.forEach(({ listener, patch }) => listener(patch))
+    // Here we subscribe our client
+    store.subscribe(listener)
+    return store.state
+}
+```
 
+```js
+// Client
+const { createStore } = require('dop')
+
+// Getting the current state of the server and subscribing to it
+const state = await subscribeToServerStore(onPatch)
+// Creates a local store where our UX components can subscribe to
+const store = createStore(state)
+
+function onPatch(patch) {
+    // Applying patch from the server
+    const listeners = store.applyPatch(patch)
+    // We emit the patch to subscribers. Like React components.
+    listeners.forEach(({ listener, patch }) => listener(patch))
+}
+```
 
+Check the website for more info [https://distributedobjectprotocol.org/](https://distributedobjectprotocol.org/guide/javascript)
 
 ## License
 
diff --git a/package.json b/package.json
index 9101c99..2d50e8d 100644
--- a/package.json
+++ b/package.json
@@ -1,69 +1,64 @@
-{
-    "name": "dop",
-    "version": "1.0.0-rc3",
-    "main": "./dist/dop.nodejs.js",
-    "browser": "./dist/dop.js",
-    "unpkg": "./dist/dop.min.js",
-    "license": "MIT",
-    "url": "http://www.distributedobjectprotocol.org",
-    "repository": {
-        "type": "git",
-        "url": "git+https://github.com/DistributedObjectProtocol/dop.git"
-    },
-    "author": "Josema Gonzalez (http://josema.es/)",
-    "description": "Distributed Object Protocol.",
-    "keywords": [
-        "realtime",
-        "pubsub",
-        "state management",
-        "data-flow",
-        "distributed",
-        "objects",
-        "protocol",
-        "reactive",
-        "sync",
-        "synchronization",
-        "rpc",
-        "remote",
-        "connect",
-        "websocket",
-        "events",
-        "sockets",
-        "xhr",
-        "server",
-        "client"
-    ],
-    "dependencies": {
-        "dop-transports": "3.0.0-rc1"
-    },
-    "devDependencies": {
-        "coveralls": "^2.11.4",
-        "electron": "^1.4.12",
-        "express": "^4.13.4",
-        "faucet": "^0.0.1",
-        "grunt": "^0.4.5",
-        "grunt-cli": "^1.2.0",
-        "grunt-contrib-concat": "^0.5.0",
-        "grunt-contrib-copy": "^0.8.2",
-        "grunt-contrib-uglify": "^0.6.0",
-        "grunt-contrib-watch": "^0.6.1",
-        "grunt-optimize-js": "^0.6.0",
-        "grunt-replace": "^1.0.1",
-        "grunt-string-replace": "^1.3.1",
-        "js-combinatorics": "^0.5.2",
-        "lodash.merge": "^4.6.0",
-        "tap": "^7.1.2",
-        "tape": "^4.6.3",
-        "tape-run": "^2.4.1"
-    },
-    "scripts": {
-        "dev": "grunt",
-        "build": "grunt --build",
-        "test": "tap test/*.js test/protocol/*.js",
-        "test:electron": "browserify test/protocol/*.js test/*.js | tape-run --render=\"faucet\"",
-        "test:chrome": "browserify test/protocol/*.js test/*.js | tape-run --render=\"faucet\" --browser chrome",
-        "test:firefox": "browserify test/protocol/*.js test/*.js | tape-run --render=\"faucet\" --browser firefox",
-        "test:safari": "browserify test/protocol/*.js test/*.js | tape-run --render=\"faucet\" --browser safari",
-        "testtravis": "tap test/*.js test/protocol/*.js"
-    }
-}
+{
+    "name": "dop",
+    "version": "1.5.4",
+    "main": "dist/dop.js",
+    "browser": "dist/dop.umd.js",
+    "module": "src/index.js",
+    "license": "MIT",
+    "url": "https://distributedobjectprotocol.org/",
+    "repository": {
+        "type": "git",
+        "url": "git+https://github.com/DistributedObjectProtocol/dop.git"
+    },
+    "author": "Josema Gonzalez (http://josema.es/)",
+    "description": "Distributed Object Protocol.",
+    "keywords": [
+        "rpc",
+        "patch",
+        "realtime",
+        "state management",
+        "distributed",
+        "pubsub",
+        "data-flow",
+        "flux",
+        "objects",
+        "protocol",
+        "reactive",
+        "sync",
+        "synchronization",
+        "remote",
+        "connect",
+        "server",
+        "client"
+    ],
+    "dependencies": {},
+    "devDependencies": {
+        "@rollup/plugin-buble": "^0.20.0",
+        "@rollup/plugin-json": "^4.0.1",
+        "ava": "^2.4.0",
+        "ejson": "^2.2.0",
+        "esm": "^3.2.25",
+        "lodash": "^4.17.15",
+        "ramda": "^0.26.1",
+        "rollup": "^1.0.0",
+        "rollup-plugin-uglify": "^6.0.4"
+    },
+    "scripts": {
+        "build": "rollup -c",
+        "dev": "rollup -c -w",
+        "test": "ava --verbose",
+        "testing": "npm test -- --watch"
+    },
+    "files": [
+        "dist",
+        "src"
+    ],
+    "ava": {
+        "files": [
+            "!test/utils.js"
+        ],
+        "require": [
+            "esm"
+        ]
+    }
+}
diff --git a/rollup.config.babel.js b/rollup.config.babel.js
new file mode 100644
index 0000000..8146bf4
--- /dev/null
+++ b/rollup.config.babel.js
@@ -0,0 +1,40 @@
+import pkg from './package.json'
+import babel from 'rollup-plugin-babel'
+import minify from 'rollup-plugin-babel-minify'
+
+const babelsetup = {
+    presets: [
+        [
+            'es2015',
+            {
+                modules: false
+            }
+        ]
+    ],
+    plugins: ['external-helpers']
+}
+
+export default [
+    // dop.js
+    {
+        input: 'src/index',
+        external: ['ms'],
+        output: [
+            { file: pkg.main, format: 'cjs' }
+            // { file: pkg.module, format: 'es' }
+        ],
+        plugins: [babel(babelsetup)]
+    },
+
+    // dop.min.js
+    {
+        input: 'src/index',
+        output: {
+            name: 'dop',
+            file: pkg.browser,
+            compact: true,
+            format: 'umd'
+        },
+        plugins: [babel(babelsetup), minify({ comments: false })]
+    }
+]
diff --git a/rollup.config.js b/rollup.config.js
new file mode 100644
index 0000000..193e9f9
--- /dev/null
+++ b/rollup.config.js
@@ -0,0 +1,52 @@
+import pkg from './package.json'
+// import minify from 'rollup-plugin-babel-minify'
+import { uglify } from 'rollup-plugin-uglify'
+import buble from '@rollup/plugin-buble'
+import json from '@rollup/plugin-json'
+import dop from './src/index'
+
+export default [
+    // dop.js
+    {
+        input: 'src/index',
+        external: ['ms'],
+        output: [
+            // { file: pkg.module, format: 'es' }
+            {
+                file: pkg.main,
+                format: 'cjs',
+                exports: 'named',
+            },
+        ],
+        plugins: [json(), buble()],
+    },
+
+    // dop.umd.js
+    {
+        input: 'src/index',
+        output: {
+            name: 'dop',
+            file: pkg.browser,
+            format: 'umd',
+            exports: 'named',
+        },
+        plugins: [
+            json(),
+            buble(),
+            uglify({
+                mangle: {
+                    reserved: Object.keys(dop.TYPE),
+                },
+            }),
+        ],
+    },
+
+    // // dop.module.js
+    // {
+    //     input: 'src/index',
+    //     external: ['ms'],
+    //     // preserveModules: true,
+    //     output: [{ dir: './module', format: 'es' }],
+    //     plugins: [json()],
+    // },
+]
diff --git a/src/api/action.js b/src/api/action.js
deleted file mode 100644
index 2b57d10..0000000
--- a/src/api/action.js
+++ /dev/null
@@ -1,13 +0,0 @@
-dop.action = function(f, filterMutationsToNode) {
-    dop.util.invariant(
-        isFunction(f),
-        'dop.action needs one argument as function'
-    )
-    return function() {
-        var collectors = dop.data.collectors
-        var collector = dop.core.createCollector(collectors, collectors.length)
-        var output = f.apply(this, arguments)
-        collector.emit(filterMutationsToNode)
-        return output
-    }
-}
diff --git a/src/api/applyPatchFactory.js b/src/api/applyPatchFactory.js
new file mode 100644
index 0000000..c8d53da
--- /dev/null
+++ b/src/api/applyPatchFactory.js
@@ -0,0 +1,84 @@
+import { isPlainObject, isPlain, isFunction } from '../util/is.js'
+import { setDeep } from '../util/getset.js'
+import { mergeCore } from '../util/merge.js'
+import { producePatch } from '../util/patches.js'
+
+export default function applyPatchFactory(patchers) {
+    function applyPatch(target, patch_or_fn) {
+        const patch = isFunction(patch_or_fn)
+            ? producePatch(target, patch_or_fn).patch
+            : patch_or_fn
+        return applyPatchRaw(target, patch)
+    }
+
+    function applyPatchRaw(target, patch) {
+        const mutations = []
+        const target_root = { '': target } // a trick to allow top level patches
+        const patch_root = { '': patch } // a trick to allow top level patches
+        const unpatch_root = { '': {} }
+
+        function addMutation(target, prop, old_value, path) {
+            mutations.push({
+                target,
+                prop,
+                old_value,
+                path,
+            })
+        }
+
+        mergeCore(patch_root, target_root, ({ patch, target, prop, path }) => {
+            const patch_value = patch[prop]
+            const target_value = target[prop]
+            if (
+                !target.hasOwnProperty(prop) ||
+                (patch_value !== target_value &&
+                    !(isPlainObject(patch_value) && isPlain(target_value)))
+            ) {
+                const length = target.length
+
+                // Applying patches
+                const old_value = patchers.reduce(
+                    (old_value, patcher) =>
+                        patcher({
+                            patch,
+                            target,
+                            prop,
+                            old_value,
+                            applyPatch,
+                        }),
+                    target_value
+                )
+
+                // We register the mutation if old_value is different to the new value
+                if (target[prop] !== old_value) {
+                    addMutation(target, prop, old_value, path.slice(1))
+                    if (target.length !== length) {
+                        addMutation(
+                            target,
+                            'length',
+                            length,
+                            path.slice(1, path.length - 1).concat('length')
+                        )
+                    }
+                }
+
+                return false // we don't go deeper
+            }
+        })
+
+        // Creating unpatch
+        for (let index = mutations.length - 1; index >= 0; --index) {
+            const { path, old_value } = mutations[index]
+            setDeep(unpatch_root, [''].concat(path), old_value)
+        }
+
+        return {
+            result: target_root[''],
+            patch,
+            unpatch: unpatch_root[''],
+            mutations,
+        }
+    }
+
+    return applyPatch
+}
diff --git a/src/api/collect.js b/src/api/collect.js
deleted file mode 100644
index b20f2ee..0000000
--- a/src/api/collect.js
+++ /dev/null
@@ -1,10 +0,0 @@
-dop.collect = function(index_function) {
-    var is_function = isFunction(index_function)
-    dop.util.invariant(
-        arguments.length == 0 || (arguments.length == 1 && is_function),
-        'dop.collect only accept one argument as function'
-    )
-    var collectors = dop.data.collectors
-    var index = is_function ? index_function(collectors) : collectors.length
-    return dop.core.createCollector(collectors, index)
-}
diff --git a/src/api/collectGetters.js b/src/api/collectGetters.js
deleted file mode 100644
index ed28a47..0000000
--- a/src/api/collectGetters.js
+++ /dev/null
@@ -1,21 +0,0 @@
-dop.collectGetters = function() {
-    var stopCollect = dop.core.collectGettersPaths()
-    return function stopCollectGetters() {
-        var objects_collected = stopCollect()
-        var output = []
-        var unique_paths = {}
-        for (var index = 0; index < objects_collected.length; ++index) {
-            var object_collected = objects_collected[index]
-            var unique_path = dop.core.pathSeparator(object_collected.path)
-            if (unique_paths[unique_path] === undefined) {
-                unique_paths[unique_path] = true
-                output.push({
-                    object: dop.getObjectProxy(object_collected.object),
-                    property: object_collected.property
-                    // path: object_collected.path
-                })
-            }
-        }
-        return output
-    }
-}
diff --git a/src/api/computed.js b/src/api/computed.js
deleted file mode 100644
index 5fc3e4c..0000000
--- a/src/api/computed.js
+++ /dev/null
@@ -1,17 +0,0 @@
-dop.computed = function(callback) {
-    dop.util.invariant(
-        isFunction(callback),
-        'dop.computed needs a function as first parameter'
-    )
-    var f = function(object, property, shall_we_set, old_value) {
-        return dop.core.createComputed(
-            object,
-            property,
-            callback,
-            shall_we_set,
-            old_value
-        )
-    }
-    f._name = dop.cons.COMPUTED_FUNCTION
-    return f
-}
diff --git a/src/api/connect.js b/src/api/connect.js
deleted file mode 100644
index e0d41b6..0000000
--- a/src/api/connect.js
+++ /dev/null
@@ -1,10 +0,0 @@
-dop.connect = function(options) {
-    if (dop.util.typeof(options) != 'object') options = {}
-
-    if (options.transport === undefined)
-        options.transport = dop.getDefaultConnectTransport()
-
-    return typeof options.transport == 'function'
-        ? options.transport(dop, options)
-        : options.transport
-}
diff --git a/src/api/createNodeFactory.js b/src/api/createNodeFactory.js
new file mode 100644
index 0000000..430f296
--- /dev/null
+++ b/src/api/createNodeFactory.js
@@ -0,0 +1,172 @@
+import { isFunction, isInteger, isArray } from '../util/is.js'
+import { createRequest, localProcedureCall } from '../util/nodes.js'
+import { RPC_CREATOR } from '../const.js'
+
+export default function createNodeFactory({ encode, decode }) {
+    return function createNode({
+        serialize = JSON.stringify,
+        deserialize = JSON.parse,
+        rpcFilter = (props) => props.rpc,
+        errorInstances = [Error],
+    } = {}) {
+        const local_rpcs_id = {}
+        const local_rpcs = new Map()
+        let local_rpc_index = 1 // 0 is reserved for the entry function used in open()
+        const requests = {}
+        let request_id_index = 0
+
+        const encode_params = {
+            local_rpcs,
+            registerLocalRpcFromEncode,
+        }
+
+        function registerRpc(function_id, fn) {
+            local_rpcs_id[function_id] = fn
+            local_rpcs.set(fn, function_id)
+            return function_id
+        }
+
+        function createRpc(function_id) {
+            const makeCall = (request_id, args) => {
+                const data = [request_id, function_id]
+                if (args.length > 0) data.push(args)
+                node.send(encode(data, encode_params))
+                return data
+            }
+
+            const rpc = (...args) => {
+                const request_id = ++request_id_index
+                const req = createRequest()
+                const { resolve, reject } = req
+                const resolveOrReject = (fn, value) => {
+                    fn(value)
+                    delete requests[request_id]
+                    return req
+                }
+                req.data = makeCall(request_id, args)
+                req.node = node
+                req.createdAt = new Date().getTime()
+                req.resolve = (value) => resolveOrReject(resolve, value)
+                req.reject = (error) => resolveOrReject(reject, error)
+                requests[request_id] = req
+                return req
+            }
+
+            rpc.push = (...args) => {
+                makeCall(0, args)
+            }
+
+            return rpc
+        }
+
+        function createRemoteFunction({
+            function_id,
+            function_creator,
+            caller,
+            path,
+        }) {
+            return rpcFilter({
+                rpc: createRpc(function_id),
+                node,
+                function_id,
+                function_creator,
+                caller,
+                path,
+            })
+        }
+
+        function getNextLocalFunctionId() {
+            while (local_rpcs_id.hasOwnProperty(local_rpc_index)) {
+                local_rpc_index += 1
+            }
+            return local_rpc_index
+        }
+
+        function open(send, fn) {
+            const function_id = 0
+            if (isFunction(fn)) registerRpc(function_id, fn)
+            node.send = (msg) => send(serialize(msg))
+            return createRemoteFunction({
+                function_id,
+                function_creator: RPC_CREATOR.ENTRY,
+            })
+        }
+
+        function message(msg) {
+            msg = deserialize(msg)
+            if (!isArray(msg) || !isInteger(msg[0])) {
+                return false
+            }
+
+            const [id, function_id] = msg
+            const is_request = id > -1
+            const fn = is_request ? local_rpcs_id[function_id] : undefined
+
+            msg = decode(msg, {
+                createRemoteFunction,
+                caller: fn,
+                function_creator: is_request
+                    ? RPC_CREATOR.REQUEST
+                    : RPC_CREATOR.RESPONSE,
+            })
+
+            let args = msg[2]
+            const response_id = -id
+
+            if (is_request && isFunction(fn)) {
+                args = isArray(msg[2]) ? msg[2] : []
+
+                // Request without response
+                if (id === 0) {
+                    const req = { node }
+                    args.push(req)
+                    fn.apply(req, args)
+                }
+
+                // Request
+                else {
+                    const req = createRequest()
+                    const response = [response_id]
+                    req.node = node
+                    req.then((value) => {
+                        response.push(0) // no errors
+                        if (value !== undefined) response.push(value)
+                        node.send(encode(response, encode_params))
+                    }).catch((error) => {
+                        response.push(error === 0 ? null : error)
+                        node.send(encode(response, encode_params))
+                    })
+                    args.push(req)
+                    localProcedureCall(fn, req, args, errorInstances)
+                }
+                return true
+            }
+
+            // Response
+            else if (id < 0 && requests.hasOwnProperty(response_id)) {
+                const response_status = function_id
+                const req = requests[response_id]
+                response_status === 0
+                    ? req.resolve(args)
+                    : req.reject(response_status)
+                return true
+            }
+
+            return false
+        }
+
+        function registerLocalRpcFromEncode(fn) {
+            return registerRpc(getNextLocalFunctionId(), fn)
+        }
+
+        const node = {
+            open,
+            message,
+            requests,
+            registerRpc,
+            createRpc,
+        }
+
+        return node
+    }
+}
diff --git a/src/api/createObserver.js b/src/api/createObserver.js
deleted file mode 100644
index 032ded3..0000000
--- a/src/api/createObserver.js
+++ /dev/null
@@ -1,16 +0,0 @@
-dop.createObserver = function(callback) {
-    dop.util.invariant(
-        isFunction(callback),
-        'dop.createObserver only accept one argument as function'
-    )
-    var observers = dop.data.observers,
-        index,
-        observer_id,
-        observer
-    for (index in observers)
-        if (observers[index].callback === callback) return observers[index]
-
-    observer_id = dop.data.observers_inc++
-    observer = new dop.core.observer(callback, observer_id)
-    return (observers[observer_id] = observer)
-}
diff --git a/src/api/createStoreFactory.js b/src/api/createStoreFactory.js
new file mode 100644
index 0000000..a820a06
--- /dev/null
+++ b/src/api/createStoreFactory.js
@@ -0,0 +1,58 @@
+import { createPatchAndUnpatchFromMutations } from '../util/patches.js'
+import { isFunction } from '../util/is.js'
+
+export default function createStoreFactory(applyPatchFunction) {
+    return function createStore(state) {
+        const listeners = new Map()
+
+        function subscribe(listener, filter) {
+            listeners.set(listener, filter)
+            return () => unsubscribe(listener) // This has become a kind of standard
+        }
+
+        function unsubscribe(listener) {
+            listeners.delete(listener)
+        }
+
+        function applyPatch(patch_or_fn) {
+            const applied = applyPatchFunction(api.state, patch_or_fn)
+
+            const outputs = Array.from(listeners.entries()).map(
+                ([listener, filter]) => {
+                    const mutations = isFunction(filter)
+                        ? applied.mutations.filter(filter)
+                        : applied.mutations.slice(0)
+
+                    const { patch, unpatch } =
+                        createPatchAndUnpatchFromMutations(
+                            mutations,
+                            applied.patch
+                        )
+
+                    return {
+                        listener,
+                        patch,
+                        unpatch,
+                        mutations,
+                    }
+                }
+            )
+
+            outputs.mutations = applied.mutations
+            outputs.patch = applied.patch
+            outputs.unpatch = applied.unpatch
+
+            return outputs
+        }
+
+        const api = {
+            state,
+            listeners,
+            subscribe,
+            unsubscribe,
+            applyPatch,
+        }
+
+        return api
+    }
+}
diff --git a/src/api/createTransport.js b/src/api/createTransport.js
deleted file mode 100644
index bbdcc45..0000000
--- a/src/api/createTransport.js
+++ /dev/null
@@ -1,3 +0,0 @@
-dop.createTransport = function(socket, close) {
-    return new dop.core.transport(socket, close)
-}
diff --git a/src/api/decode.js b/src/api/decode.js
deleted file mode 100644
index e0d49fa..0000000
--- a/src/api/decode.js
+++ /dev/null
@@ -1,13 +0,0 @@
-dop.decode = function(data) {
-    var undefineds = [],
-        index = 0,
-        total,
-        output = JSON.parse(data, function(property, value) {
-            return dop.core.decode.call(this, property, value, undefineds)
-        })
-
-    for (total = undefineds.length, index = 0; index < total; ++index)
-        undefineds[index][0][undefineds[index][1]] = undefined
-
-    return output
-}
diff --git a/src/api/del.js b/src/api/del.js
deleted file mode 100644
index 97090b2..0000000
--- a/src/api/del.js
+++ /dev/null
@@ -1,6 +0,0 @@
-dop.del = function(object, property) {
-    // dop.util.invariant(dop.isRegistered(object), 'Object passed to dop.del must be a registered object');
-    return dop.isRegistered(object)
-        ? dop.core.delete(object, property) !== undefined
-        : delete object[property]
-}
diff --git a/src/api/encode.js b/src/api/encode.js
deleted file mode 100644
index da63f61..0000000
--- a/src/api/encode.js
+++ /dev/null
@@ -1,7 +0,0 @@
-dop.encode = function(data, encoder) {
-    if (typeof encoder != 'function') encoder = dop.core.encode
-    return JSON.stringify(data, encoder)
-}
-dop.encodeFunction = function(data) {
-    return JSON.stringify(data, dop.core.encodeFunction)
-}
diff --git a/src/api/get.js b/src/api/get.js
deleted file mode 100644
index 108231a..0000000
--- a/src/api/get.js
+++ /dev/null
@@ -1,7 +0,0 @@
-dop.get = function(object, property) {
-    // dop.util.invariant(dop.isRegistered(object), 'Object passed to dop.del must be a registered object');
-    if (dop.data.gets_collecting && dop.isRegistered(object))
-        dop.core.proxyObjectHandler.get(dop.getObjectTarget(object), property)
-
-    return object[property]
-}
diff --git a/src/api/getNode.js b/src/api/getNode.js
deleted file mode 100644
index fc59f18..0000000
--- a/src/api/getNode.js
+++ /dev/null
@@ -1,3 +0,0 @@
-dop.getNode = function(args) {
-    return dop.getRequest(args).node
-}
diff --git a/src/api/getObject.js b/src/api/getObject.js
deleted file mode 100644
index 603e37c..0000000
--- a/src/api/getObject.js
+++ /dev/null
@@ -1,33 +0,0 @@
-dop.getObjectDop = function(object) {
-    return object[dop.cons.DOP]
-}
-
-dop.getObjectRoot = function(object) {
-    return dop.getObjectDop(object).r
-}
-
-dop.getObjectParent = function(object) {
-    return dop.getObjectDop(object)._
-}
-
-dop.getObjectProxy = function(object) {
-    return dop.getObjectDop(object).p
-}
-
-dop.getObjectTarget = function(object) {
-    return dop.getObjectDop(object).t
-}
-
-dop.getObjectProperty = function(object) {
-    var object_dop = dop.getObjectDop(object)
-    if (isArray(object_dop._)) dop.getObjectPath(object)
-    return object_dop.pr
-}
-
-dop.getObjectId = function(object) {
-    return dop.getObjectProperty(dop.getObjectRoot(object))
-}
-
-dop.getObjectLevel = function(object) {
-    return dop.getObjectDop(object).l
-}
diff --git a/src/api/getObjectPath.js b/src/api/getObjectPath.js
deleted file mode 100644
index 65d7240..0000000
--- a/src/api/getObjectPath.js
+++ /dev/null
@@ -1,34 +0,0 @@
-dop.getObjectPath = function(object, strict) {
-    var path = [],
-        // path_id = '',
-        parent,
-        prop,
-        object_dop = object[dop.cons.DOP]
-
-    strict = strict !== false
-
-    while (object_dop._ !== undefined) {
-        prop = object_dop.pr
-        parent = dop.getObjectTarget(object_dop._)
-        if (!strict || parent[prop] === object_dop.p) {
-            path.unshift(prop)
-            object_dop = parent[dop.cons.DOP]
-            // path_id = dop.core.pathSeparator(prop)+path_id;
-        } else {
-            if (isArray(parent)) {
-                prop = parent.indexOf(object_dop.p)
-                if (prop === -1) return
-                else object_dop.pr = prop
-                // path.unshift(prop);
-            } else return
-        }
-    }
-
-    path.unshift(object_dop.pr)
-    // path.path_id = dop.core.pathSeparator(object_dop.pr)+path_id;
-    return path
-}
-
-// dop.getObjectPathId = function(object) {
-//     return dop.core.getPathId(dop.getObjectPath(object));
-// };
diff --git a/src/api/getObjectPathId.js b/src/api/getObjectPathId.js
deleted file mode 100644
index e5083ed..0000000
--- a/src/api/getObjectPathId.js
+++ /dev/null
@@ -1,3 +0,0 @@
-dop.getObjectPathId = function(object) {
-    return dop.core.getPathId(dop.getObjectPath(object))
-}
diff --git a/src/api/getRequest.js b/src/api/getRequest.js
deleted file mode 100644
index a7f5f99..0000000
--- a/src/api/getRequest.js
+++ /dev/null
@@ -1,12 +0,0 @@
-dop.getRequest = function(args) {
-    args = Array.prototype.slice.call(args, 0)
-    var request = args[args.length - 1]
-    if (
-        request instanceof Promise &&
-        request.hasOwnProperty('resolve') &&
-        request.hasOwnProperty('reject')
-    )
-        return request
-
-    return dop.core.createAsync()
-}
diff --git a/src/api/intercept.js b/src/api/intercept.js
deleted file mode 100644
index d4c7bef..0000000
--- a/src/api/intercept.js
+++ /dev/null
@@ -1,38 +0,0 @@
-dop.intercept = function(object, property, callback) {
-    dop.util.invariant(
-        dop.isRegistered(object),
-        'dop.intercept() needs a registered object as first parameter'
-    )
-    var path = dop.getObjectPath(object)
-    dop.util.invariant(
-        isArray(path),
-        'dop.intercept() The object you are passing is not allocated to a registered object'
-    )
-    var type = 'interceptors'
-    if (arguments.length === 2) callback = property
-
-    dop.util.invariant(
-        isFunction(callback),
-        'dop.intercept() needs a callback as last parameter'
-    )
-
-    var path_id = dop.core.getPathId(path),
-        data_path = dop.data.path
-
-    if (arguments.length === 3) {
-        type = 'interceptors_prop'
-        path_id += dop.core.pathSeparator(property)
-    }
-
-    if (data_path[path_id] === undefined) data_path[path_id] = {}
-
-    if (data_path[path_id][type] === undefined) data_path[path_id][type] = []
-
-    var interceptors = data_path[path_id][type]
-    interceptors.push(callback)
-
-    return function dispose() {
-        // delete interceptors[interceptors.indexOf(callback)]; // we splice in dop.core.runInterceptors
-        interceptors.splice(interceptors.indexOf(callback), 1)
-    }
-}
diff --git a/src/api/isBroadcastFunction.js b/src/api/isBroadcastFunction.js
deleted file mode 100644
index 47559d6..0000000
--- a/src/api/isBroadcastFunction.js
+++ /dev/null
@@ -1,3 +0,0 @@
-dop.isBroadcastFunction = function(fun) {
-    return isFunction(fun) && fun._name === dop.cons.BROADCAST_FUNCTION
-}
diff --git a/src/api/isObjectRegistrable.js b/src/api/isObjectRegistrable.js
deleted file mode 100644
index b06db73..0000000
--- a/src/api/isObjectRegistrable.js
+++ /dev/null
@@ -1,3 +0,0 @@
-dop.isObjectRegistrable = function(object) {
-    return isObject(object) && !isArray(object)
-}
diff --git a/src/api/isPojoObject.js b/src/api/isPojoObject.js
deleted file mode 100644
index 6884dcf..0000000
--- a/src/api/isPojoObject.js
+++ /dev/null
@@ -1,23 +0,0 @@
-dop.isPojoObject = function(object) {
-    if (object === null || typeof object !== 'object') return false
-    var prototype = Object.getPrototypeOf(object)
-    return prototype === Object.prototype || prototype === Array.prototype
-}
-
-// dop.isPojoObject = function(object) {
-//     var tof = dop.util.typeof(object);
-//     return (tof === 'object' || tof == 'array');
-// };
-
-// function Test(){}
-// console.log(dop.isPojoObject({}));
-// console.log(dop.isPojoObject([]));
-// console.log(dop.isPojoObject(new Error));
-// console.log(dop.isPojoObject(new Date()));
-// console.log(dop.isPojoObject(null));
-// console.log(dop.isPojoObject(Symbol('')));
-// console.log(dop.isPojoObject(function(){}));
-// console.log(dop.isPojoObject(1));
-// console.log(dop.isPojoObject("s"));
-// console.log(dop.isPojoObject(true));
-// console.log(dop.isPojoObject(/a/));
diff --git a/src/api/isRegistered.js b/src/api/isRegistered.js
deleted file mode 100644
index 74bade9..0000000
--- a/src/api/isRegistered.js
+++ /dev/null
@@ -1,7 +0,0 @@
-dop.isRegistered = function(object) {
-    return (
-        isObject(object) &&
-        dop.getObjectDop(object) !== undefined &&
-        !Object.getOwnPropertyDescriptor(object, dop.cons.DOP).enumerable
-    )
-}
diff --git a/src/api/isRemoteFunction.js b/src/api/isRemoteFunction.js
deleted file mode 100644
index a2c3c24..0000000
--- a/src/api/isRemoteFunction.js
+++ /dev/null
@@ -1,3 +0,0 @@
-dop.isRemoteFunction = function(fun) {
-    return isFunction(fun) && fun._name === dop.cons.REMOTE_FUNCTION
-}
diff --git a/src/api/listen.js b/src/api/listen.js
deleted file mode 100644
index 976eb50..0000000
--- a/src/api/listen.js
+++ /dev/null
@@ -1,10 +0,0 @@
-dop.listen = function(options) {
-    if (dop.util.typeof(options) != 'object') options = {}
-
-    if (options.transport === undefined)
-        options.transport = dop.getDefaultListenTransport()
-
-    return typeof options.transport == 'function'
-        ? options.transport(dop, options)
-        : options.transport
-}
diff --git a/src/api/onsubscribe.js b/src/api/onsubscribe.js
deleted file mode 100644
index e662856..0000000
--- a/src/api/onsubscribe.js
+++ /dev/null
@@ -1,7 +0,0 @@
-dop.onSubscribe = function(callback) {
-    dop.util.invariant(
-        isFunction(callback),
-        'dop.onSubscribe only accept a function as parameter'
-    )
-    dop.data.onsubscribe = callback
-}
diff --git a/src/api/register.js b/src/api/register.js
deleted file mode 100644
index 6dc8db1..0000000
--- a/src/api/register.js
+++ /dev/null
@@ -1,9 +0,0 @@
-dop.register = function(object) {
-    dop.util.invariant(
-        dop.isObjectRegistrable(object),
-        'dop.register needs an object as first parameter'
-    )
-    return dop.isRegistered(object)
-        ? dop.getObjectProxy(object)
-        : dop.core.configureObject(object, dop.data.object_inc++)
-}
diff --git a/src/api/removeComputed.js b/src/api/removeComputed.js
deleted file mode 100644
index 0b3940b..0000000
--- a/src/api/removeComputed.js
+++ /dev/null
@@ -1,62 +0,0 @@
-dop.removeComputed = function(object, property, callback) {
-    dop.util.invariant(
-        dop.isRegistered(object),
-        'dop.removeComputed needs a registered object as first parameter'
-    )
-    dop.util.invariant(
-        property !== undefined,
-        'dop.removeComputed needs a string or number as second parameter'
-    )
-
-    var computed_pathid = dop.core.getPathId(
-            dop.getObjectPath(object, false).concat(property)
-        ),
-        shall_we_remove_all = !isFunction(callback),
-        is_same_function,
-        data_path = dop.data.path,
-        removed = [],
-        computed_ids,
-        computed_id,
-        computed,
-        derivation_pathid,
-        derivations,
-        index,
-        // total,
-        index2,
-        total2
-
-    if (
-        isObject(data_path[computed_pathid]) &&
-        isArray(data_path[computed_pathid].computeds) &&
-        data_path[computed_pathid].computeds.length > 0
-    ) {
-        computed_ids = data_path[computed_pathid].computeds
-        for (index = 0; index < computed_ids.length; ++index) {
-            computed_id = computed_ids[index]
-            computed = dop.data.computed[computed_id]
-            is_same_function = computed.function === callback
-            if (shall_we_remove_all || is_same_function) {
-                // Deleting computing itself
-                delete dop.data.computed[computed_id]
-                // Removing id in computed
-                computed_ids.splice(computed_ids.indexOf(computed_id), 1)
-                // Removing derivations
-                for (
-                    index2 = 0, total2 = computed.derivations.length;
-                    index2 < total2;
-                    ++index2
-                ) {
-                    derivation_pathid = computed.derivations[index2]
-                    derivations = data_path[derivation_pathid].derivations
-                    derivations.splice(derivations.indexOf(computed_id), 1)
-                }
-                index -= 1
-                removed.push(computed.function)
-            }
-
-            if (is_same_function) break
-        }
-    }
-
-    return removed
-}
diff --git a/src/api/set.js b/src/api/set.js
deleted file mode 100644
index 5b137c1..0000000
--- a/src/api/set.js
+++ /dev/null
@@ -1,7 +0,0 @@
-dop.set = function(object, property, value, options) {
-    // dop.util.invariant(dop.isRegistered(object), 'Object passed to dop.set must be a registered object');
-    dop.isRegistered(object)
-        ? dop.core.set(object, property, value, options)
-        : (object[property] = value)
-    return value
-}
diff --git a/src/api/setBroadcastFunction.js b/src/api/setBroadcastFunction.js
deleted file mode 100644
index 7850b45..0000000
--- a/src/api/setBroadcastFunction.js
+++ /dev/null
@@ -1,14 +0,0 @@
-dop.setBroadcastFunction = function(object, namefunction) {
-    dop.util.invariant(
-        dop.isRegistered(object),
-        'Object passed to dop.setBroadcastFunction must be a registered object'
-    )
-    var path = dop.getObjectPath(object),
-        object_id = path.shift()
-    path.push(namefunction)
-    dop.getObjectTarget(object)[namefunction] = function() {
-        return dop.protocol.broadcast(object_id, path, arguments)
-    }
-    dop.getObjectTarget(object)[namefunction]._name =
-        dop.cons.BROADCAST_FUNCTION
-}
diff --git a/src/const.js b/src/const.js
new file mode 100644
index 0000000..e60add0
--- /dev/null
+++ b/src/const.js
@@ -0,0 +1,13 @@
+export const ESCAPE_KEY = '$escape'
+export const RPC_KEY = '$r'
+export const DELETE_KEY = '$d'
+export const REPLACE_KEY = '$e'
+export const SPLICE_KEY = '$s'
+export const SWAP_KEY = '$w'
+export const MULTI_KEY = '$m'
+
+export const RPC_CREATOR = {
+    ENTRY: 'ENTRY',
+    REQUEST: 'REQUEST',
+    RESPONSE: 'RESPONSE',
+}
diff --git a/src/core/constructors/collector.js b/src/core/constructors/collector.js
deleted file mode 100644
index 9de4762..0000000
--- a/src/core/constructors/collector.js
+++ /dev/null
@@ -1,44 +0,0 @@
-dop.core.collector = function Collector(queue, index) {
-    this.active = true
-    this.mutations = []
-    this.queue = queue
-    queue.splice(index, 0, this)
-}
-
-dop.core.collector.prototype.add = function(mutation) {
-    if (
-        this.active &&
-        (this.filter === undefined || this.filter(mutation) === true)
-    ) {
-        this.mutations.push(mutation)
-        return true
-    }
-    return false
-}
-
-dop.core.collector.prototype.emit = function(filterMutationsToNode) {
-    this.destroy()
-    return this.emitWithoutDestroy(filterMutationsToNode)
-}
-
-dop.core.collector.prototype.emitWithoutDestroy = function(
-    filterMutationsToNode
-) {
-    var snapshot = new dop.core.snapshot(this.mutations)
-    snapshot.emit(filterMutationsToNode)
-    this.mutations = []
-    return snapshot
-}
-
-dop.core.collector.prototype.pause = function() {
-    this.active = false
-}
-
-dop.core.collector.prototype.resume = function() {
-    this.active = true
-}
-
-dop.core.collector.prototype.destroy = function() {
-    this.active = false
-    this.queue.splice(this.queue.indexOf(this), 1)
-}
diff --git a/src/core/constructors/node.js b/src/core/constructors/node.js
deleted file mode 100644
index fb591cb..0000000
--- a/src/core/constructors/node.js
+++ /dev/null
@@ -1,41 +0,0 @@
-dop.core.node = function Node(transport) {
-    // Inherit emitter
-    dop.util.merge(this, new dop.util.emitter()) //https://jsperf.com/inheritance-call-vs-object-assign
-    this.transport = transport
-    this.connected = false
-    this.request_inc = 1
-    this.requests = {}
-    this.request_queue = [] // [<request>, <wrapper>]
-    this.subscriber = {}
-    this.owner = {}
-    // this.sends_queue = []
-}
-
-dop.core.node.prototype.send = function(message) {
-    return this.sendSocket(message)
-    // if (
-    //     this.status !== dop.cons.NODE_STATUS_CONNECTED ||
-    //     this.sends_queue.length > 0 ||
-    //     this.sendSocket(message) === false
-    // ) {
-    //     this.sends_queue.push(message)
-    //     return false
-    // }
-    // return true
-}
-
-dop.core.node.prototype.disconnect = function() {
-    return this.disconnectSocket()
-}
-
-dop.core.node.prototype.subscribe = function() {
-    return dop.protocol.subscribe(this, arguments)
-}
-
-dop.core.node.prototype.unsubscribe = function(object) {
-    dop.util.invariant(
-        dop.isRegistered(object),
-        'Node.unsubscribe needs a subscribed object'
-    )
-    return dop.protocol.unsubscribe(this, object)
-}
diff --git a/src/core/constructors/observer.js b/src/core/constructors/observer.js
deleted file mode 100644
index 030a2a1..0000000
--- a/src/core/constructors/observer.js
+++ /dev/null
@@ -1,99 +0,0 @@
-dop.core.observer = function Observer(callback, id) {
-    this.callback = callback
-    this.id = id
-    this.observers_object = {} // need it for destroy()
-    this.observers_prop = {} // need it for destroy()
-    this.observers_all = {} // need it for destroy()
-}
-
-dop.core.observer.prototype.observeProperty = function(object, property) {
-    var path = observerCheckObject(object, 'observeProperty')
-    dop.util.invariant(
-        arguments.length === 2,
-        'observer.observeProperty() You must pass a name property as second parameter'
-    )
-    return this.observe(
-        dop.core.getPathId(path) + dop.core.pathSeparator(property),
-        'observers_prop'
-    )
-}
-
-dop.core.observer.prototype.observeObject = function(object) {
-    var path = observerCheckObject(object, 'observeObject')
-    return this.observe(dop.core.getPathId(path), 'observers_object')
-}
-
-dop.core.observer.prototype.observeAll = function(object) {
-    var path = observerCheckObject(object, 'observeAll')
-    return this.observe(dop.core.getPathId(path), 'observers_all')
-}
-
-dop.core.observer.prototype.observe = function(path_id, type) {
-    var data_path = dop.data.path
-
-    if (data_path[path_id] === undefined) data_path[path_id] = {}
-
-    if (data_path[path_id][type] === undefined) data_path[path_id][type] = {}
-
-    data_path[path_id][type][this.id] = true
-    this[type][path_id] = true
-
-    return function unobserve() {
-        delete data_path[path_id][type][this.id]
-        delete this[type][path_id]
-    }.bind(this)
-}
-
-dop.core.observer.prototype.destroy = function() {
-    var path_id
-    var data_path = dop.data.path
-    var path_ids = {}
-
-    // Removing observeProperty
-    for (path_id in this.observers_prop) {
-        path_ids[path_id] = true
-        delete data_path[path_id].observers_prop[this.id]
-        if (isEmptyObject(data_path[path_id].observers_prop))
-            delete data_path[path_id].observers_prop
-    }
-
-    // Removing observeObject
-    for (path_id in this.observers_object) {
-        path_ids[path_id] = true
-        delete data_path[path_id].observers_object[this.id]
-        if (isEmptyObject(data_path[path_id].observers_object))
-            delete data_path[path_id].observers_object
-    }
-
-    // Removing observeAll
-    for (path_id in this.observers_all) {
-        path_ids[path_id] = true
-        delete data_path[path_id].observers_all[this.id]
-        if (isEmptyObject(data_path[path_id].observers_all))
-            delete data_path[path_id].observers_all
-    }
-
-    // Removing path_ids
-    for (path_id in path_ids) {
-        if (isEmptyObject(data_path[path_id])) {
-            delete data_path[path_id]
-        }
-    }
-
-    delete dop.data.observers[this.id]
-}
-
-function observerCheckObject(object, method) {
-    dop.util.invariant(
-        dop.isRegistered(object),
-        'observer.' + method + '() needs a registered object as first parameter'
-    )
-    var path = dop.getObjectPath(object)
-    dop.util.invariant(
-        isArray(path),
-        'observer.' +
-            method +
-            '() The object you are passing is not allocated to a registered object'
-    )
-    return path
-}
diff --git a/src/core/constructors/snapshot.js b/src/core/constructors/snapshot.js
deleted file mode 100644
index e3aef81..0000000
--- a/src/core/constructors/snapshot.js
+++ /dev/null
@@ -1,47 +0,0 @@
-dop.core.snapshot = function Snapshot(mutations) {
-    this.mutations = mutations
-    this.forward = true
-}
-
-dop.core.snapshot.prototype.undo = function() {
-    if (this.forward && this.mutations.length > 0) {
-        this.forward = false
-        this.setPatch(this.getUnpatch())
-    }
-}
-
-dop.core.snapshot.prototype.redo = function() {
-    if (!this.forward && this.mutations.length > 0) {
-        this.forward = true
-        this.setPatch(this.getPatch())
-    }
-}
-
-dop.core.snapshot.prototype.emit = function(filterMutationsToNode) {
-    if (this.mutations.length > 0) {
-        dop.core.emitToObservers(this, filterMutationsToNode)
-    }
-}
-
-dop.core.snapshot.prototype.getPatch = function() {
-    return (this.patch =
-        this.patch === undefined
-            ? dop.core.getPatch(this.mutations)
-            : this.patch)
-}
-
-dop.core.snapshot.prototype.getUnpatch = function() {
-    return (this.unpatch =
-        this.unpatch === undefined
-            ? dop.core.getUnpatch(this.mutations)
-            : this.unpatch)
-}
-
-dop.core.snapshot.prototype.setPatch = function(patch) {
-    for (var object_id in patch)
-        dop.core.setPatch(
-            patch[object_id].object,
-            patch[object_id].chunks,
-            dop.core.setPatchMutator
-        )
-}
diff --git a/src/core/constructors/transport.js b/src/core/constructors/transport.js
deleted file mode 100644
index cab7445..0000000
--- a/src/core/constructors/transport.js
+++ /dev/null
@@ -1,37 +0,0 @@
-dop.core.transport = function Transport(socket) {
-    dop.util.merge(this, new dop.util.emitter()) // Inherit emitter
-    this.socket = socket
-}
-
-dop.core.transport.prototype.onCreate = function(
-    socket,
-    sendSocket,
-    disconnectSocket
-) {
-    var node = new dop.core.node(this)
-    node.socket = socket
-    node.sendSocket = sendSocket
-    node.disconnectSocket = disconnectSocket
-    dop.core.registerNode(node)
-    return node
-}
-
-dop.core.transport.prototype.onConnect = function(node) {
-    node.connected = true
-    this.emit(dop.cons.ON_CONNECT, node)
-}
-
-dop.core.transport.prototype.onMessage = function(node, message) {
-    dop.core.onMessage(node, message)
-    this.emit(dop.cons.ON_MESSAGE, node, message)
-}
-
-dop.core.transport.prototype.onDisconnect = function(node) {
-    node.connected = false
-    dop.core.unregisterNode(node)
-    this.emit(dop.cons.ON_DISCONNECT, node)
-}
-
-dop.core.transport.prototype.onError = function(node) {
-    this.emit(dop.cons.ON_ERROR, node)
-}
diff --git a/src/core/error.js b/src/core/error.js
deleted file mode 100644
index 0c68c04..0000000
--- a/src/core/error.js
+++ /dev/null
@@ -1,23 +0,0 @@
-dop.core.error = {
-    // warning: {
-    //     TOKEN_REJECTED: 'User disconnected because is rejecting too many times the token assigned'
-    // },
-
-    reject_local: {
-        OBJECT_NOT_FOUND: 'Object not found',
-        NODE_NOT_FOUND: 'Node not found',
-        TIMEOUT_REQUEST: 'Timeout waiting for response'
-    },
-
-    // Remote rejects
-    reject_remote: {
-        OBJECT_NOT_FOUND: 1,
-        1: 'Remote object not found or not permissions to use it',
-        SUBSCRIPTION_NOT_FOUND: 2,
-        2: 'Subscription not found to unsubscribe this object',
-        FUNCTION_NOT_FOUND: 3,
-        3: 'Remote function not found to be called',
-        CUSTOM_REJECTION: 4
-        // 4: ''
-    }
-}
diff --git a/src/core/mutators/delete.js b/src/core/mutators/delete.js
deleted file mode 100644
index aead166..0000000
--- a/src/core/mutators/delete.js
+++ /dev/null
@@ -1,24 +0,0 @@
-dop.core.delete = function(object, property) {
-    var descriptor = Object.getOwnPropertyDescriptor(object, property)
-    if (descriptor && descriptor.configurable) {
-        var object_target = dop.getObjectTarget(object),
-            object_proxy = dop.getObjectProxy(object),
-            path,
-            old_value = object_target[property],
-            deleted = delete object_target[property]
-
-        if (
-            (object_target === object_proxy || object === object_proxy) &&
-            (path = dop.getObjectPath(object))
-        )
-            dop.core.storeMutation({
-                object: dop.getObjectProxy(object_target),
-                prop: String(property),
-                path: path,
-                old_value: dop.util.clone(old_value)
-            })
-
-        // needed for dop.core.proxyObjectHandler.deleteProperty
-        return deleted
-    }
-}
diff --git a/src/core/mutators/pop.js b/src/core/mutators/pop.js
deleted file mode 100644
index 2184f1c..0000000
--- a/src/core/mutators/pop.js
+++ /dev/null
@@ -1,5 +0,0 @@
-dop.core.pop = function(array) {
-    if (array.length === 0) return undefined
-    var spliced = dop.core.splice(array, [array.length - 1, 1])
-    return spliced[0]
-}
diff --git a/src/core/mutators/push.js b/src/core/mutators/push.js
deleted file mode 100644
index d796c83..0000000
--- a/src/core/mutators/push.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// https://jsperf.com/push-against-splice OR https://jsperf.com/push-vs-splice
-dop.core.push = function(array, items) {
-    if (items.length === 0) return array.length
-    items.unshift(array.length, 0)
-    dop.core.splice(array, items)
-    return array.length
-}
diff --git a/src/core/mutators/reverse.js b/src/core/mutators/reverse.js
deleted file mode 100644
index 616b202..0000000
--- a/src/core/mutators/reverse.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// https://jsperf.com/array-reverse-algorithm
-dop.core.reverse = function(array) {
-    var object_target = dop.getObjectTarget(array),
-        object_proxy = dop.getObjectProxy(array),
-        path
-
-    if (
-        (object_target === object_proxy || array === object_proxy) &&
-        (path = dop.getObjectPath(array))
-    ) {
-        var total = object_target.length / 2,
-            index = 0,
-            indexr,
-            temp_item,
-            swaps = []
-
-        for (; index < total; ++index) {
-            indexr = object_target.length - index - 1
-            if (index !== indexr) {
-                temp_item = object_target[indexr]
-                object_target[indexr] = object_target[index]
-                object_target[index] = temp_item
-                swaps.push(index, indexr)
-            }
-        }
-
-        if (swaps.length > 0)
-            dop.core.storeMutation({
-                object: object_proxy,
-                prop: dop.getObjectProperty(array),
-                path: path,
-                swaps: swaps
-            })
-    } else Array.prototype.reverse.call(object_target)
-
-    return array
-}
diff --git a/src/core/mutators/set.js b/src/core/mutators/set.js
deleted file mode 100644
index fce5486..0000000
--- a/src/core/mutators/set.js
+++ /dev/null
@@ -1,84 +0,0 @@
-dop.core.set = function(object, property, value, options) {
-    if (!isObject(options)) options = {}
-
-    options.deep = typeof options.deep == 'boolean' ? options.deep : true
-    options.shadow = typeof options.shadow == 'boolean' ? options.shadow : false
-
-    // If is a different value
-    if (object[property] !== value || !object.hasOwnProperty(property)) {
-        var descriptor = Object.getOwnPropertyDescriptor(object, property)
-
-        if (!descriptor || (descriptor && descriptor.writable)) {
-            var object_target = dop.getObjectTarget(object),
-                object_proxy = dop.getObjectProxy(object),
-                old_value = object_target[property],
-                length = object_target.length,
-                is_new_property = !object_target.hasOwnProperty(property),
-                object_is_array = isArray(object_target),
-                path
-
-            // if (object_is_array)
-            //     property = Number(property);
-
-            // object or array
-            if (
-                options.deep &&
-                dop.isPojoObject(value) &&
-                !(
-                    dop.isRegistered(value) &&
-                    dop.getObjectParent(value) === object_proxy
-                )
-            )
-                object_target[property] = dop.core.configureObject(
-                    value,
-                    property,
-                    object_proxy
-                )
-            // computed value
-            else if (
-                isFunction(value) &&
-                value._name == dop.cons.COMPUTED_FUNCTION
-            )
-                object_target[property] = value(
-                    object_target,
-                    property,
-                    false,
-                    old_value
-                )
-            // other
-            else object_target[property] = value
-
-            if (
-                !options.shadow &&
-                (object_target === object_proxy || object === object_proxy) &&
-                !(isFunction(old_value) && isFunction(value)) &&
-                (path = dop.getObjectPath(object))
-            ) {
-                var mutation = {
-                    object: object_proxy,
-                    prop: object_is_array ? String(property) : property,
-                    path: path,
-                    value: dop.util.clone(value)
-                }
-                if (!is_new_property)
-                    mutation.old_value = dop.util.clone(old_value)
-
-                dop.core.storeMutation(mutation)
-
-                // If is array and length is different we must store the length
-                if (
-                    property !== 'length' &&
-                    object_target.length !== length &&
-                    object_is_array
-                )
-                    dop.core.storeMutation({
-                        object: object_proxy,
-                        prop: 'length',
-                        path: path,
-                        value: object_target.length,
-                        old_value: length
-                    })
-            }
-        }
-    }
-}
diff --git a/src/core/mutators/shift.js b/src/core/mutators/shift.js
deleted file mode 100644
index 2312b24..0000000
--- a/src/core/mutators/shift.js
+++ /dev/null
@@ -1,5 +0,0 @@
-dop.core.shift = function(array) {
-    if (array.length === 0) return undefined
-    var spliced = dop.core.splice(array, [0, 1])
-    return spliced[0]
-}
diff --git a/src/core/mutators/sort.js b/src/core/mutators/sort.js
deleted file mode 100644
index 33071b4..0000000
--- a/src/core/mutators/sort.js
+++ /dev/null
@@ -1,85 +0,0 @@
-// http://stackoverflow.com/a/234777/1469219 http://stackoverflow.com/a/38905402/1469219
-// https://en.wikipedia.org/wiki/Sorting_algorithm#Stability
-// http://khan4019.github.io/front-end-Interview-Questions/sort.html#bubbleSort
-// https://github.com/benoitvallon/computer-science-in-javascript/tree/master/sorting-algorithms-in-javascript
-dop.core.sort = function(array, compareFunction) {
-    var object_target = dop.getObjectTarget(array),
-        object_proxy = dop.getObjectProxy(array),
-        copy = object_target.slice(0),
-        output,
-        swaps,
-        path
-
-    output = Array.prototype.sort.call(object_target, compareFunction)
-
-    if (
-        (object_target === object_proxy || array === object_proxy) &&
-        (path = dop.getObjectPath(array))
-    ) {
-        swaps = dop.core.sortDiff(object_target, copy)
-        if (swaps.length > 1)
-            dop.core.storeMutation({
-                object: dop.getObjectProxy(array),
-                prop: dop.getObjectProperty(array),
-                path: path,
-                swaps: swaps
-            })
-    }
-
-    return output
-}
-
-dop.core.sortDiff = function(array, copy) {
-    var total = copy.length,
-        swaps = [],
-        index1 = 0,
-        index2,
-        tmp
-
-    for (; index1 < total; ++index1) {
-        if (array[index1] !== copy[index1]) {
-            index2 = copy.indexOf(array[index1])
-            tmp = copy[index1]
-            copy[index1] = copy[index2]
-            copy[index2] = tmp
-            swaps.push(index1, index2)
-        }
-    }
-
-    return swaps
-}
-
-// function diffArray(array) {
-//     var copy = array.slice(0),
-//         swaps = [],
-//         index = 0,
-//         total = copy.length,
-//         indexNew, tmp;
-
-//     array.sort();
-
-//     for (;index<total; ++index) {
-//         if (copy[index] !== array[index]) {
-//             indexNew = copy.indexOf(array[index]);
-//             tmp = copy[index];
-//             copy[index] = copy[indexNew];
-//             copy[indexNew] = tmp;
-//             swaps.push([index, indexNew]);
-
-//             console.log([index, indexNew], copy );
-//             if (indexNew < index) {
-//                 console.log( 'lol' );
-//             }
-
-//             // swapeds[indexNew] = true;
-//             // if (indexCache!==indexNew && indexCache !== index) {
-//             //     swapeds[indexCache] = true;
-//             //     swap(copy, indexNew, indexCache);
-//             //     swaps.push([indexNew, indexCache]);
-//             //     console.log([indexNew, indexCache], copy, swapeds );
-//             // }
-//         }
-//     }
-
-//     return swaps;
-// }
diff --git a/src/core/mutators/splice.js b/src/core/mutators/splice.js
deleted file mode 100644
index 3c19448..0000000
--- a/src/core/mutators/splice.js
+++ /dev/null
@@ -1,70 +0,0 @@
-dop.core.splice = function(array, args) {
-    var object_target = dop.getObjectTarget(array),
-        object_proxy = dop.getObjectProxy(array),
-        original_length = object_target.length,
-        spliced,
-        path
-
-    // Splicing!!
-    spliced = Array.prototype.splice.apply(object_target, args)
-
-    // If enviroment do not allow proxies (object_target and object_proxy are same object in that case)
-    // or if the array is the proxy itself
-    path = dop.getObjectPath(array)
-    if (path) {
-        var argslength = args.length,
-            length = object_target.length,
-            index = 2,
-            start = Number(args[0]),
-            // delete_count = (Number(args[1])>0) ? args[1] : 0,
-            itemslength = args.length > 2 ? args.length - 2 : 0,
-            item
-
-        // Defaults for start
-        if (isNaN(start)) start = 0
-        else if (start < 0) start = length + start < 0 ? 0 : length + start
-        else if (start > original_length) start = original_length
-
-        // // We dont need update becase no items remaining after splice
-        // end = (argslength===1) ? 0 :
-        //     // If delete_count is the same of items to add means the new lengh is the same and we only need to update the new elements
-        //     (argslength>2 && delete_count===itemslength) ?
-        //         start+delete_count
-        //     :
-        //         object_target.length;
-
-        // We must register new objects
-        for (; index < argslength; ++index, ++start) {
-            item = args[index]
-            if (dop.isPojoObject(item))
-                object_target[start] = dop.core.configureObject(
-                    item,
-                    start,
-                    object_proxy
-                )
-        }
-
-        // Storing mutation
-        if (
-            (object_target === object_proxy || array === object_proxy) &&
-            (original_length !== length || itemslength > 0)
-        ) {
-            if (args[0] < 0) args[0] = array.length + args[0]
-            var mutation = {
-                object: object_proxy,
-                prop: dop.getObjectProperty(array),
-                path: path,
-                splice: args
-            }
-
-            if (spliced.length > 0) mutation.spliced = dop.util.clone(spliced)
-
-            if (length !== original_length)
-                mutation.old_length = original_length
-
-            dop.core.storeMutation(mutation)
-        }
-    }
-
-    return spliced
-}
diff --git a/src/core/mutators/swap.js b/src/core/mutators/swap.js
deleted file mode 100644
index 9ab1af9..0000000
--- a/src/core/mutators/swap.js
+++ /dev/null
@@ -1,16 +0,0 @@
-dop.core.swap = function(array, swaps) {
-    var object_target = dop.getObjectTarget(array),
-        object_proxy = dop.getObjectProxy(array)
-
-    var result = dop.util.swap(object_target, swaps)
-
-    if (object_target === object_proxy || array === object_proxy)
-        dop.core.storeMutation({
-            object: object_proxy,
-            prop: dop.getObjectProperty(array),
-            path: dop.getObjectPath(array),
-            swaps: swaps.slice(0)
-        })
-
-    return result
-}
diff --git a/src/core/mutators/unshift.js b/src/core/mutators/unshift.js
deleted file mode 100644
index 96730d1..0000000
--- a/src/core/mutators/unshift.js
+++ /dev/null
@@ -1,6 +0,0 @@
-dop.core.unshift = function(array, items) {
-    if (items.length === 0) return array.length
-    items.unshift(0, 0)
-    dop.core.splice(array, items)
-    return array.length
-}
diff --git a/src/core/objects/collectGettersPaths.js b/src/core/objects/collectGettersPaths.js
deleted file mode 100644
index cb7a99c..0000000
--- a/src/core/objects/collectGettersPaths.js
+++ /dev/null
@@ -1,12 +0,0 @@
-dop.core.collectGettersPaths = function() {
-    var data = dop.data
-    var gets_paths = data.gets_paths
-    var items = []
-    data.gets_collecting = true
-    gets_paths.push(items)
-    return function stopCollectGetters() {
-        gets_paths.splice(gets_paths.indexOf(items), 1)
-        data.gets_collecting = false
-        return items
-    }
-}
diff --git a/src/core/objects/configureObject.js b/src/core/objects/configureObject.js
deleted file mode 100644
index 1aade45..0000000
--- a/src/core/objects/configureObject.js
+++ /dev/null
@@ -1,102 +0,0 @@
-var can_we_proxy = typeof Proxy == 'function'
-dop.core.configureObject = function(object, property_parent, parent) {
-    // Creating a copy if is another object registered
-    if (dop.isRegistered(object))
-        return dop.core.configureObject(
-            dop.util.clone(object),
-            property_parent,
-            parent
-        )
-
-    // Setting ~DOP object
-    var object_dop = {},
-        object_proxy,
-        object_target
-    object_dop._ = parent // parent
-    object_dop.pr = isArray(parent) ? Number(property_parent) : property_parent // property
-
-    // Making proxy object
-    if (can_we_proxy) {
-        object_proxy = object_dop.p = new Proxy(
-            object,
-            dop.core.proxyObjectHandler
-        )
-        object_target = object_dop.t = object
-    } else object_proxy = object_target = object_dop.p = object_dop.t = object
-
-    // root
-    object_dop.r =
-        parent === undefined ? object_proxy : dop.getObjectDop(parent).r
-
-    // // Object parent level and more
-    // if (parent === undefined) {
-    //     object_dop.r = object_proxy;  // root
-    //     object_dop.l = 1; // deep level [1,"prop","arr"] this is level 3
-    //     object_dop.ia = false; // is inside of array
-    // }
-    // else {
-    //     var object_dop_parent = dop.getObjectDop(parent);
-    //     object_dop.l = object_dop_parent.l+1;  // deep level [1,"prop","arr"] this is level 3
-    //     object_dop.ia = (object_dop_parent.ia || isArray(parent)); // is inside of array
-    // }
-
-    Object.defineProperty(object_target, dop.cons.DOP, {
-        value: object_dop,
-        enumerable: false,
-        configurable: false,
-        writable: false
-    })
-
-    // Deep objects (Recursion)
-    var property,
-        value,
-        path,
-        is_array = isArray(object_target),
-        is_function,
-        object_descriptor
-
-    for (property in object_target) {
-        if (is_array) {
-            property = Number(property)
-        }
-        object_descriptor = Object.getOwnPropertyDescriptor(
-            object_target,
-            property
-        )
-
-        if (object_descriptor !== undefined) {
-            value =
-                typeof object_descriptor.get == 'function'
-                    ? dop.computed(object_descriptor.get)
-                    : object_target[property]
-            is_function = isFunction(value)
-
-            // computed value
-            if (is_function && value._name == dop.cons.COMPUTED_FUNCTION) {
-                delete object_target[property] // we need this line in order to remove the defineProperty of get syntax
-                object_target[property] = value(
-                    object_proxy,
-                    property,
-                    false,
-                    undefined
-                )
-            }
-            // object or array
-            else if (dop.isPojoObject(value))
-                object_target[property] = dop.core.configureObject(
-                    value,
-                    property,
-                    object_proxy
-                )
-        }
-    }
-
-    // if (isObject(parent))
-    // object_dop._ = (dop.isRegistered(parent)) ? dop.getObjectTarget(parent) : parent;
-
-    // Adding traps for mutations methods of arrays
-    if (dop.util.typeof(object_target) == 'array')
-        Object.defineProperties(object_target, dop.core.proxyArrayHandler)
-
-    return object_proxy
-}
diff --git a/src/core/objects/configureRemoteFunctions.js b/src/core/objects/configureRemoteFunctions.js
deleted file mode 100644
index 8db8342..0000000
--- a/src/core/objects/configureRemoteFunctions.js
+++ /dev/null
@@ -1,10 +0,0 @@
-dop.core.configureRemoteFunctions = function(node, object, object_owner_id) {
-    dop.util.path(object, function(source, prop, value, destiny, path) {
-        if (
-            isFunction(value) &&
-            value._name == dop.cons.REMOTE_FUNCTION_UNSETUP
-        ) {
-            dop.set(source, prop, value(node, object_owner_id, path.slice(0)))
-        }
-    })
-}
diff --git a/src/core/objects/createCollector.js b/src/core/objects/createCollector.js
deleted file mode 100644
index 8538c5d..0000000
--- a/src/core/objects/createCollector.js
+++ /dev/null
@@ -1,4 +0,0 @@
-dop.core.createCollector = function(queue, index) {
-    var collector = new dop.core.collector(queue, index)
-    return collector
-}
diff --git a/src/core/objects/createComputed.js b/src/core/objects/createComputed.js
deleted file mode 100644
index fe4b1fd..0000000
--- a/src/core/objects/createComputed.js
+++ /dev/null
@@ -1,29 +0,0 @@
-dop.core.createComputed = function(object, prop, f, shall_we_set, old_value) {
-    var data_path = dop.data.path,
-        value,
-        computed_id = dop.data.computed_inc++,
-        computed = {
-            object_root: dop.getObjectRoot(object),
-            prop: prop,
-            function: f,
-            derivations: []
-        },
-        path = dop.getObjectPath(object, false)
-
-    computed.path = path.slice(1)
-    computed.pathid = dop.core.getPathId(path.concat(prop))
-
-    if (data_path[computed.pathid] === undefined)
-        data_path[computed.pathid] = {}
-
-    if (data_path[computed.pathid].computeds === undefined)
-        data_path[computed.pathid].computeds = []
-
-    dop.data.computed[computed_id] = computed
-    value = dop.core.updateComputed(computed_id, computed, object, old_value)
-
-    // Setting value
-    if (shall_we_set) dop.core.set(object, prop, value)
-
-    return value
-}
diff --git a/src/core/objects/emitToObservers.js b/src/core/objects/emitToObservers.js
deleted file mode 100644
index ef71820..0000000
--- a/src/core/objects/emitToObservers.js
+++ /dev/null
@@ -1,83 +0,0 @@
-dop.core.emitToObservers = function(snapshot, filterMutationsToNode) {
-    var mutations = snapshot.mutations,
-        mutation,
-        path_id,
-        observer_id,
-        mutations_by_observers = {},
-        mutations_with_subscribers = false,
-        data_path = dop.data.path,
-        index = 0,
-        total = mutations.length,
-        path,
-        path_index,
-        path_id_all
-
-    for (; index < total; ++index) {
-        mutation = mutations[index]
-        path_id = mutation.path_id
-        path = mutation.path
-
-        // .observers_object
-        if (
-            data_path[path_id] !== undefined &&
-            data_path[path_id].observers_object !== undefined
-        ) {
-            for (observer_id in data_path[path_id].observers_object) {
-                if (mutations_by_observers[observer_id] === undefined)
-                    mutations_by_observers[observer_id] = []
-                mutations_by_observers[observer_id].push(mutation)
-            }
-        }
-
-        // .observers_all
-        for (path_index = path.length; path_index > 0; --path_index) {
-            path_id_all = dop.core.getPathId(path.slice(0, path_index))
-            if (
-                data_path[path_id_all] !== undefined &&
-                data_path[path_id_all].observers_all !== undefined
-            ) {
-                for (observer_id in data_path[path_id_all].observers_all) {
-                    if (mutations_by_observers[observer_id] === undefined)
-                        mutations_by_observers[observer_id] = []
-                    mutations_by_observers[observer_id].push(mutation)
-                }
-            }
-        }
-
-        // .observers_prop
-        if (mutation.swaps === undefined) {
-            // If mutation is swaps type we should skip because does not have observers_prop and also the length never changes
-            path_id += dop.core.pathSeparator(
-                mutation.splice === undefined ? mutation.prop : 'length'
-            )
-            if (
-                data_path[path_id] !== undefined &&
-                data_path[path_id].observers_prop !== undefined
-            ) {
-                for (observer_id in data_path[path_id].observers_prop) {
-                    if (mutations_by_observers[observer_id] === undefined)
-                        mutations_by_observers[observer_id] = []
-                    // We have to check this because we dont want to duplicate
-                    if (
-                        mutations_by_observers[observer_id].indexOf(mutation) ==
-                        -1
-                    )
-                        mutations_by_observers[observer_id].push(mutation)
-                }
-            }
-        }
-    }
-
-    // Emiting
-    for (observer_id in mutations_by_observers) {
-        var observer = dop.data.observers[observer_id]
-        if (observer !== undefined)
-            // We need to make sure that the observer still exists, because maybe has been removed after calling previous observers
-            observer.callback(
-                mutations_by_observers[observer_id],
-                filterMutationsToNode
-            )
-    }
-
-    return mutations_with_subscribers
-}
diff --git a/src/core/objects/getMutationInverted.js b/src/core/objects/getMutationInverted.js
deleted file mode 100644
index 11f17af..0000000
--- a/src/core/objects/getMutationInverted.js
+++ /dev/null
@@ -1,37 +0,0 @@
-dop.core.getMutationInverted = function(mutation) {
-    var mutation_inverted = {
-        object: mutation.object,
-        path: mutation.path,
-        prop: mutation.prop
-    }
-
-    // splice
-    if (mutation.splice !== undefined) {
-        var splice = mutation.splice,
-            spliced = mutation.spliced === undefined ? [] : mutation.spliced
-
-        mutation_inverted.splice = [splice[0], splice.length - 2]
-        Array.prototype.push.apply(mutation_inverted.splice, spliced)
-
-        mutation_inverted.spliced = splice.slice(2)
-        if (mutation_inverted.spliced.length === 0)
-            delete mutation_inverted.spliced
-    }
-
-    // swaps
-    else if (mutation.swaps !== undefined)
-        mutation_inverted.swaps = mutation.swaps.slice(0).reverse()
-    // new value
-    else if (!mutation.hasOwnProperty('old_value'))
-        mutation_inverted.old_value = mutation.value
-    // delete
-    else if (!mutation.hasOwnProperty('value'))
-        mutation_inverted.value = mutation.old_value
-    // set
-    else {
-        mutation_inverted.old_value = mutation.value
-        mutation_inverted.value = mutation.old_value
-    }
-
-    return mutation_inverted
-}
diff --git a/src/core/objects/getPatch.js b/src/core/objects/getPatch.js
deleted file mode 100644
index 01caa9d..0000000
--- a/src/core/objects/getPatch.js
+++ /dev/null
@@ -1,23 +0,0 @@
-dop.core.getPatch = function(mutations, is_unpatch) {
-    var patchs = {},
-        index = 0,
-        total = mutations.length,
-        mutation,
-        object_id
-
-    for (; index < total; ++index) {
-        mutation = is_unpatch
-            ? dop.core.getMutationInverted(mutations[index])
-            : mutations[index]
-        object_id = dop.getObjectId(mutation.object)
-        if (patchs[object_id] === undefined)
-            patchs[object_id] = {
-                chunks: [{}],
-                object: dop.getObjectRoot(mutation.object)
-            }
-        dop.core.injectMutationInPatch(patchs[object_id], mutation)
-        // console.log(JSON.stringify(patchs[object_id].chunks))
-    }
-
-    return patchs
-}
diff --git a/src/core/objects/getPathId.js b/src/core/objects/getPathId.js
deleted file mode 100644
index 9601ea7..0000000
--- a/src/core/objects/getPathId.js
+++ /dev/null
@@ -1,10 +0,0 @@
-dop.core.getPathId = function(path) {
-    var index = 0,
-        total = path.length,
-        path_id = ''
-
-    for (; index < total; ++index)
-        path_id += dop.core.pathSeparator(path[index])
-
-    return path_id
-}
diff --git a/src/core/objects/getUnpatch.js b/src/core/objects/getUnpatch.js
deleted file mode 100644
index b51e0f7..0000000
--- a/src/core/objects/getUnpatch.js
+++ /dev/null
@@ -1,3 +0,0 @@
-dop.core.getUnpatch = function(mutations) {
-    return dop.core.getPatch(mutations.slice(0).reverse(), true)
-}
diff --git a/src/core/objects/injectMutationInPatch.js b/src/core/objects/injectMutationInPatch.js
deleted file mode 100644
index e71c87a..0000000
--- a/src/core/objects/injectMutationInPatch.js
+++ /dev/null
@@ -1,102 +0,0 @@
-dop.core.injectMutationInPatch = function(patch, mutation) {
-    var prop = mutation.prop,
-        path = mutation.path,
-        value = mutation.value,
-        is_mutation_delete = !mutation.hasOwnProperty('value'),
-        is_mutation_splice = mutation.splice !== undefined,
-        is_mutation_swaps = mutation.swaps !== undefined,
-        is_mutation_array = is_mutation_splice || is_mutation_swaps,
-        tof_value = dop.util.typeof(value),
-        index = 1,
-        chunk = patch.chunks[patch.chunks.length - 1],
-        chunk_parent = chunk,
-        chunk_next = {},
-        chunk_next_parent = chunk_next,
-        chunk_next_root = chunk_next,
-        tof_current_object,
-        special_instruction,
-        instructions_patchs = dop.protocol.instructionsPatchs,
-        is_new_object = false,
-        is_new_chunk = false,
-        prop_path,
-        value_merged,
-        newspecial_instruction
-
-    // Going deep
-    for (; index < path.length; ++index) {
-        prop_path = path[index]
-        chunk_parent = chunk
-        chunk_next_parent = chunk_next
-        chunk_next = chunk_next[prop_path] = {}
-        tof_current_object = dop.util.typeof(chunk[prop_path])
-
-        if (tof_current_object == 'array') {
-            special_instruction = chunk[prop_path]
-            // Is a new object
-            if (special_instruction[0] === instructions_patchs.object) {
-                is_new_object = true
-                chunk = special_instruction[1]
-            } else if (
-                !is_mutation_array ||
-                (is_mutation_array && index + 1 < path.length)
-            ) {
-                is_new_chunk = true
-                chunk = chunk_next
-                patch.chunks.push(chunk_next_root)
-            }
-        } else if (
-            !is_new_chunk &&
-            is_mutation_array &&
-            tof_current_object == 'object'
-        ) {
-            // is_new_chunk = true;
-            chunk_parent = chunk_next_parent
-            chunk = chunk_next
-            patch.chunks.push(chunk_next_root)
-        } else if (tof_current_object == 'object') chunk = chunk[prop_path]
-        else chunk = chunk[prop_path] = {}
-    }
-
-    /// INJECTING ///
-
-    // Objects or array
-    if (tof_value == 'object' || tof_value == 'array') {
-        value_merged = dop.util.merge(tof_value == 'array' ? [] : {}, value)
-        if (is_new_object) chunk[prop] = value_merged
-        else {
-            chunk[prop] = [instructions_patchs.object, value_merged]
-        }
-    }
-
-    // Mutations over arrays
-    else if (is_mutation_array) {
-        if (is_new_object)
-            is_mutation_splice
-                ? Array.prototype.splice.apply(chunk, mutation.splice.slice(0))
-                : dop.util.swap(chunk, mutation.swaps.slice(0))
-        else {
-            newspecial_instruction = is_mutation_splice
-                ? [instructions_patchs.splice, mutation.splice.slice(0)]
-                : [instructions_patchs.swaps, mutation.swaps.slice(0)]
-
-            if (!isArray(chunk_parent[prop]))
-                chunk_parent[prop] = newspecial_instruction
-            else {
-                if (isNumber(chunk_parent[prop][0]))
-                    chunk_parent[prop] = [chunk_parent[prop]]
-
-                chunk_parent[prop].push(newspecial_instruction)
-            }
-        }
-    }
-
-    // Delete
-    else if (is_mutation_delete) {
-        chunk[prop] = [instructions_patchs.delete]
-    }
-
-    // Others values
-    else {
-        chunk[prop] = value
-    }
-}
diff --git a/src/core/objects/mergeSubscription.js b/src/core/objects/mergeSubscription.js
deleted file mode 100644
index 232de9c..0000000
--- a/src/core/objects/mergeSubscription.js
+++ /dev/null
@@ -1,17 +0,0 @@
-dop.core.mergeSubscription = function(object, object_remote) {
-    return dop.util.path(
-        object_remote,
-        null,
-        object,
-        dop.util.mergeMutator
-        // dop.core.mergeSubscriptionMutator
-    )
-}
-
-// dop.core.mergeSubscriptionMutator = function(destiny, prop, value, tof_value) {
-//     if (tof_value == 'object' || tof_value == 'array')
-//         !destiny.hasOwnProperty(prop)
-//             ? (destiny[prop] = tof_value == 'array' ? [] : {})
-//             : destiny[prop]
-//     else destiny[prop] = value
-// }
diff --git a/src/core/objects/pathSeparator.js b/src/core/objects/pathSeparator.js
deleted file mode 100644
index a76399d..0000000
--- a/src/core/objects/pathSeparator.js
+++ /dev/null
@@ -1,3 +0,0 @@
-dop.core.pathSeparator = function(property) {
-    return property + '.' + property
-}
diff --git a/src/core/objects/proxyArrayHandler.js b/src/core/objects/proxyArrayHandler.js
deleted file mode 100644
index b0423a2..0000000
--- a/src/core/objects/proxyArrayHandler.js
+++ /dev/null
@@ -1,53 +0,0 @@
-// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype#Mutator_methods
-dop.core.proxyArrayHandler = {
-    splice: {
-        value: function() {
-            return dop.core.splice(
-                this,
-                Array.prototype.slice.call(arguments, 0)
-            )
-        }
-    },
-    shift: {
-        value: function() {
-            return dop.core.shift(
-                this,
-                Array.prototype.slice.call(arguments, 0)
-            )
-        }
-    },
-    pop: {
-        value: function() {
-            return dop.core.pop(this, Array.prototype.slice.call(arguments, 0))
-        }
-    },
-    push: {
-        value: function() {
-            return dop.core.push(this, Array.prototype.slice.call(arguments, 0))
-        }
-    },
-    unshift: {
-        value: function() {
-            return dop.core.unshift(
-                this,
-                Array.prototype.slice.call(arguments, 0)
-            )
-        }
-    },
-    reverse: {
-        value: function() {
-            return dop.core.reverse(this)
-        }
-    },
-    sort: {
-        value: function(compareFunction) {
-            return dop.core.sort(this, compareFunction)
-        }
-    }
-    /*fill: {value:function() {
-        return dop.core.fill.apply(this, arguments);
-    }},
-    copyWithin: {value:function() {
-        return dop.core.copyWithin.apply(this, arguments);
-    }},*/
-}
diff --git a/src/core/objects/proxyObjectHandler.js b/src/core/objects/proxyObjectHandler.js
deleted file mode 100644
index ec2523d..0000000
--- a/src/core/objects/proxyObjectHandler.js
+++ /dev/null
@@ -1,35 +0,0 @@
-dop.core.proxyObjectHandler = {
-    set: function(object, property, value) {
-        dop.core.set(dop.getObjectProxy(object), property, value)
-        return true
-    },
-    deleteProperty: function(object, property) {
-        dop.core.delete(dop.getObjectProxy(object), property) !== undefined
-        return true
-    },
-    get: function(object, property) {
-        if (
-            dop.data.gets_collecting &&
-            typeof property == 'string' &&
-            property !== dop.cons.DOP &&
-            object[property] !== Array.prototype[property]
-        ) {
-            var index = dop.data.gets_paths.length - 1
-            dop.data.gets_paths[index].push({
-                object: object,
-                property: property,
-                path: dop.getObjectPath(object, false).concat(property)
-            })
-        }
-
-        return object[property]
-    }
-}
-/*var gets_paths = dop.data.gets_paths,
-                last_path = gets_paths[gets_paths.length-1],
-                path = dop.getObjectPath(object).concat(property);
-
-            if (gets_paths.length>0 && path.length>last_path.length)
-                gets_paths.pop();
-            
-            gets_paths.push(path);*/
diff --git a/src/core/objects/runDerivations.js b/src/core/objects/runDerivations.js
deleted file mode 100644
index 81c6fd3..0000000
--- a/src/core/objects/runDerivations.js
+++ /dev/null
@@ -1,31 +0,0 @@
-dop.core.runDerivations = function(path_id) {
-    if (
-        dop.data.path[path_id] !== undefined &&
-        dop.data.path[path_id].derivations !== undefined
-    ) {
-        var derivations = dop.data.path[path_id].derivations,
-            computed,
-            computed_id,
-            object,
-            total = derivations.length,
-            index = 0
-
-        for (; index < total; ++index) {
-            computed_id = derivations[index]
-            computed = dop.data.computed[computed_id]
-            object = dop.util.get(computed.object_root, computed.path)
-            if (object !== undefined)
-                dop.core.set(
-                    object,
-                    computed.prop,
-                    // computed.function.call(object, object[computed.prop])
-                    dop.core.updateComputed(
-                        computed_id,
-                        computed,
-                        object,
-                        object[computed.prop]
-                    )
-                )
-        }
-    }
-}
diff --git a/src/core/objects/setPatch.js b/src/core/objects/setPatch.js
deleted file mode 100644
index e3f5b2f..0000000
--- a/src/core/objects/setPatch.js
+++ /dev/null
@@ -1,142 +0,0 @@
-dop.core.setPatch = function(object, patch, mutator) {
-    if (!isArray(patch)) patch = [patch]
-
-    for (var index = 0, total = patch.length; index < total; ++index)
-        dop.util.path(patch[index], null, object, mutator)
-
-    return object
-}
-
-dop.core.setPatchFunctionMutator = function(
-    destiny,
-    prop,
-    value,
-    tof_value,
-    path
-) {
-    if (isFunction(value) && value._name == dop.cons.REMOTE_FUNCTION_UNSETUP)
-        dop.set(destiny, prop, value(dop.getObjectId(destiny), path.slice(0)))
-    else return dop.core.setPatchMutator(destiny, prop, value, tof_value, path)
-}
-
-dop.core.setPatchMutator = function(destiny, prop, value, tof_value) {
-    var type_instruction,
-        instructions_patchs = dop.protocol.instructionsPatchs,
-        mutation
-
-    // Array instruction
-    if (tof_value == 'array') {
-        type_instruction = value[0]
-
-        // Delete
-        if (type_instruction === instructions_patchs.delete) {
-            dop.del(destiny, prop)
-        }
-
-        // New object/array
-        else if (type_instruction === instructions_patchs.object) {
-            dop.set(destiny, prop, dop.util.clone(value[1]))
-        }
-
-        // Array mutations
-        else {
-            // We must create the array in case the
-            if (!destiny.hasOwnProperty(prop)) {
-                dop.set(destiny, prop, [])
-            }
-
-            if (!isArray(type_instruction)) value = [value]
-
-            for (var index = 0, total = value.length; index < total; ++index) {
-                mutation = value[index]
-
-                // Splice
-                if (mutation[0] === instructions_patchs.splice)
-                    dop.core.splice(destiny[prop], mutation[1])
-                else if (mutation[0] === instructions_patchs.swaps)
-                    dop.core.swap(destiny[prop], mutation[1])
-            }
-        }
-
-        return true // Skiping to dont go inside of [instructionPatch, ...]
-    }
-
-    // Any value
-    if (tof_value != 'object' && tof_value != 'array') {
-        dop.set(destiny, prop, value)
-    }
-
-    // Not defined object
-    else if (!destiny.hasOwnProperty(prop)) {
-        dop.set(destiny, prop, {})
-    }
-}
-
-// // Array mutations
-// if (tof_value=='object' && typeofDestiny=='array' && value.hasOwnProperty(dop.cons.DOP)) {
-
-//     var mutations = value[dop.cons.DOP],
-//         mutation,
-//         index=0,
-//         total=mutations.length,
-//         typeArrayMutation;
-
-//     // if (typeofDestiny!='array')
-//     //     dop.set(destiny, prop, []);
-
-//     for (;index<total; ++index) {
-//         typeArrayMutation = mutations[index][0]; // 0=swaps 1=splices
-//         mutation = mutations[index].slice(1);
-
-//         // swap
-//         if (typeArrayMutation===0)
-//             dop.core.swap(destiny[prop], mutation);
-
-//         // length
-//         else if (typeArrayMutation===2)
-//             dop.set(destiny[prop], 'length', mutation[0]);
-
-//         // splice & set & del
-//         else {
-//             // We have to update the length of the array in case that is lower than before
-//             if (destiny[prop].length<mutation[0])
-//                 dop.getObjectTarget(destiny[prop]).length = mutation[0];
-
-//             // set
-//             if (mutation.length===3 && mutation[1]===1) {
-//                 (mutation[2] === undefined) ?
-//                     dop.del(destiny[prop], mutation[0])
-//                 :
-//                     dop.set(destiny[prop], mutation[0], mutation[2]);
-//             }
-
-//             // splice
-//             else
-//                 dop.core.splice(destiny[prop], mutation);
-//         }
-//     }
-
-//     // if (typeof value.length == 'number' && value.length>-1)
-//         // destiny[prop].length = value.length;
-
-//     return true; // Skiping to dont go inside of {~dop:...}
-// }
-
-// else //if (path.length > 1) {
-
-//     // Objects
-//     if (tof_value=='object' && typeofDestiny!='object') //!destiny.hasOwnProperty(prop)
-//         dop.set(destiny, prop, {});
-
-//     // Arrays
-//     else if (tof_value=='array' && typeofDestiny!='array')
-//         dop.set(destiny, prop, []);
-
-//     // Delete
-//     else if (tof_value=='undefined')
-//         dop.del(destiny, prop);
-
-//     // Set value
-//     else if (tof_value!='object')
-//         dop.set(destiny, prop, value);
-// //}
diff --git a/src/core/objects/storeMutation.js b/src/core/objects/storeMutation.js
deleted file mode 100644
index 5050ae0..0000000
--- a/src/core/objects/storeMutation.js
+++ /dev/null
@@ -1,80 +0,0 @@
-dop.core.storeMutation = function(mutation) {
-    var collectors = dop.data.collectors,
-        paths = dop.data.path,
-        index = 0,
-        total = collectors.length,
-        path_id_parent,
-        path_id
-
-    // Saving path_id
-    path_id_parent = path_id = mutation.path_id = dop.core.getPathId(
-        mutation.path
-    )
-
-    if (mutation.splice === undefined && mutation.swaps === undefined)
-        path_id += dop.core.pathSeparator(mutation.prop)
-
-    // Interceptors objects
-    if (
-        !dop.core.runInterceptors(
-            paths[path_id_parent],
-            'interceptors',
-            mutation
-        )
-    )
-        return
-
-    // Interceptors properties
-    if (
-        !dop.core.runInterceptors(paths[path_id], 'interceptors_prop', mutation)
-    )
-        return
-
-    // Collectors
-    for (; index < total; index++) {
-        if (collectors[index].add(mutation)) {
-            dop.core.runDerivations(path_id_parent)
-            dop.core.runDerivations(path_id)
-            return
-        }
-    }
-
-    var snapshot = new dop.core.snapshot([mutation])
-    snapshot.emit()
-
-    dop.core.runDerivations(path_id_parent)
-    dop.core.runDerivations(path_id)
-}
-
-dop.core.runInterceptors = function(interceptors, type, mutation) {
-    if (
-        interceptors &&
-        (interceptors = interceptors[type]) &&
-        interceptors.length > 0
-    )
-        for (var index = 0, total = interceptors.length; index < total; ++index)
-            if (
-                interceptors[index](
-                    mutation,
-                    dop.getObjectTarget(mutation.object)
-                ) !== true
-            )
-                return false
-
-    return true
-}
-
-// dop.core.runInterceptors = function(interceptors, type, mutation) {
-//     if (interceptors && (interceptors=interceptors[type]) && interceptors.length>0) {
-//         for (var index=0,total=interceptors.length, tosplice=[]; index<total; ++index) {
-//             if (interceptors[index] === undefined)
-//                 tosplice.push(index);
-//             else if (interceptors[index](mutation) !== true)
-//                 return false;
-//         }
-//         for (index=0,total=tosplice.length; index<total; ++index)
-//             tosplice.splice(tosplice[index], 1);
-//     }
-
-//     return true;
-// };
diff --git a/src/core/objects/updateComputed.js b/src/core/objects/updateComputed.js
deleted file mode 100644
index e3d756c..0000000
--- a/src/core/objects/updateComputed.js
+++ /dev/null
@@ -1,51 +0,0 @@
-dop.core.updateComputed = function(computed_id, computed, context, old_value) {
-    var data_path = dop.data.path,
-        derived_paths,
-        derived_pathsids = computed.derivations,
-        derived_path,
-        derived_pathid,
-        value,
-        index = 0,
-        total,
-        index2,
-        total2
-
-    // Running function and saving paths from getters
-    var stopCollector = dop.core.collectGettersPaths()
-    value = computed.function.call(context, old_value)
-    derived_paths = stopCollector()
-
-    // Generating and storing paths ids
-    for (total = derived_paths.length; index < total; ++index) {
-        derived_path = derived_paths[index].path
-        derived_pathid = ''
-        for (
-            index2 = 0, total2 = derived_path.length;
-            index2 < total2;
-            ++index2
-        ) {
-            derived_pathid += dop.core.pathSeparator(derived_path[index2])
-            if (index2 > 0) {
-                if (data_path[derived_pathid] === undefined)
-                    data_path[derived_pathid] = {}
-
-                if (data_path[derived_pathid].derivations === undefined)
-                    data_path[derived_pathid].derivations = []
-
-                if (
-                    data_path[derived_pathid].derivations.indexOf(computed_id) <
-                    0
-                ) {
-                    data_path[derived_pathid].derivations.push(computed_id)
-                    derived_pathsids.push(derived_pathid)
-                }
-            }
-        }
-    }
-
-    // Storing computed in dop.data
-    if (data_path[computed.pathid].computeds.indexOf(computed_id) === -1)
-        data_path[computed.pathid].computeds.push(computed_id)
-
-    return value
-}
diff --git a/src/core/protocol/createAsync.js b/src/core/protocol/createAsync.js
deleted file mode 100644
index 09fce24..0000000
--- a/src/core/protocol/createAsync.js
+++ /dev/null
@@ -1,11 +0,0 @@
-dop.core.createAsync = function() {
-    var resolve,
-        reject,
-        promise = new Promise(function(res, rej) {
-            resolve = res
-            reject = rej
-        })
-    promise.resolve = resolve
-    promise.reject = reject
-    return promise
-}
diff --git a/src/core/protocol/createRemoteFunction.js b/src/core/protocol/createRemoteFunction.js
deleted file mode 100644
index 2bfddfb..0000000
--- a/src/core/protocol/createRemoteFunction.js
+++ /dev/null
@@ -1,16 +0,0 @@
-dop.core.createRemoteFunction = function() {
-    var f = function(node, object_owner_id, path) {
-        var f2 = function $DOP_REMOTE_FUNCTION() {
-            return dop.protocol.call(node, object_owner_id, path, arguments)
-        }
-        f2._name = dop.cons.REMOTE_FUNCTION
-        return f2
-    }
-    f._name = dop.cons.REMOTE_FUNCTION_UNSETUP
-    return f
-}
-
-// // http://jsperf.com/dynamic-name-of-functions
-// return new Function(
-//     "var a=arguments;return function " + path[path.length-1] + "(){return a[0](a[1], a[2], a[3], arguments)}"
-// )(dop.protocol.call, node, object_id, path)
diff --git a/src/core/protocol/createRequest.js b/src/core/protocol/createRequest.js
deleted file mode 100644
index a2d4972..0000000
--- a/src/core/protocol/createRequest.js
+++ /dev/null
@@ -1,10 +0,0 @@
-dop.core.createRequest = function(node) {
-    var request_id = node.request_inc++,
-        request = Array.prototype.slice.call(arguments, 1)
-
-    node.requests[request_id] = request
-    request.unshift(request_id)
-    request.promise = dop.core.createAsync()
-
-    return request
-}
diff --git a/src/core/protocol/createResponse.js b/src/core/protocol/createResponse.js
deleted file mode 100644
index 17f7b6b..0000000
--- a/src/core/protocol/createResponse.js
+++ /dev/null
@@ -1,4 +0,0 @@
-dop.core.createResponse = function() {
-    arguments[0] = arguments[0] * -1
-    return Array.prototype.slice.call(arguments, 0)
-}
diff --git a/src/core/protocol/decode.js b/src/core/protocol/decode.js
deleted file mode 100644
index bedd0ac..0000000
--- a/src/core/protocol/decode.js
+++ /dev/null
@@ -1,36 +0,0 @@
-var regexpdate = /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d.\d\d\dZ$/,
-    regexpsplit = /\/(.+)\/([gimuy]{0,5})/
-
-dop.core.decode = function(property, value, undefineds) {
-    if (typeof value == 'string') {
-        if (value == dop.protocol.instructionsPatchs.function)
-            return dop.core.createRemoteFunction()
-
-        if (
-            value == dop.protocol.instructionsPatchs.undefined &&
-            isObject(undefineds)
-        ) {
-            undefineds.push([this, property]) // http://stackoverflow.com/questions/17648150/how-does-json-parse-manage-undefined
-            return undefined
-        }
-
-        if (value == dop.protocol.instructionsPatchs.infinity) return Infinity
-
-        if (value == dop.protocol.instructionsPatchs._infinity) return -Infinity
-
-        if (value == dop.protocol.instructionsPatchs.nan) return NaN
-
-        if (regexpdate.exec(value)) return new Date(value)
-
-        if (value.substr(0, 2) == dop.protocol.instructionsPatchs.regex) {
-            var split = regexpsplit.exec(value.substr(2)) // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/RegExp
-            return new RegExp(split[1], split[2])
-        }
-
-        if (value[0] == '~')
-            // https://jsperf.com/charat-vs-index/5
-            return value.substring(1)
-    }
-
-    return value
-}
diff --git a/src/core/protocol/deleteRequest.js b/src/core/protocol/deleteRequest.js
deleted file mode 100644
index 39e488b..0000000
--- a/src/core/protocol/deleteRequest.js
+++ /dev/null
@@ -1,4 +0,0 @@
-dop.core.deleteRequest = function(node, request_id) {
-    clearTimeout(node.requests[request_id].timeout)
-    delete node.requests[request_id]
-}
diff --git a/src/core/protocol/encode.js b/src/core/protocol/encode.js
deleted file mode 100644
index e33ca45..0000000
--- a/src/core/protocol/encode.js
+++ /dev/null
@@ -1,29 +0,0 @@
-dop.core.encode = function(property, value) {
-    var tof = typeof value
-
-    if (tof == 'undefined')
-        // http://stackoverflow.com/questions/17648150/how-does-json-parse-manage-undefined
-        return dop.protocol.instructionsPatchs.undefined
-
-    if (tof == 'string' && value[0] == '~') return '~' + value
-
-    if (tof == 'number' && isNaN(value))
-        return dop.protocol.instructionsPatchs.nan
-
-    if (tof == 'object' && value instanceof RegExp)
-        return dop.protocol.instructionsPatchs.regex + value.toString()
-
-    if (value === Infinity) return dop.protocol.instructionsPatchs.infinity
-
-    if (value === -Infinity) return dop.protocol.instructionsPatchs._infinity
-
-    return value
-}
-
-// // Extending example
-// var encode = dop.core.encodeUtil;
-// dop.core.encodeUtil = function(property, value) {
-//     if (typeof value == 'boolean')
-//         return '~BOOL';
-//     return encode(property, value);
-// };
diff --git a/src/core/protocol/encodeFunction.js b/src/core/protocol/encodeFunction.js
deleted file mode 100644
index b2cc24a..0000000
--- a/src/core/protocol/encodeFunction.js
+++ /dev/null
@@ -1,5 +0,0 @@
-dop.core.encodeFunction = function(property, value) {
-    return isFunction(value) && !dop.isBroadcastFunction(value)
-        ? dop.protocol.instructionsPatchs.function
-        : dop.core.encode(property, value)
-}
diff --git a/src/core/protocol/getRejectError.js b/src/core/protocol/getRejectError.js
deleted file mode 100644
index 63beab2..0000000
--- a/src/core/protocol/getRejectError.js
+++ /dev/null
@@ -1,11 +0,0 @@
-dop.core.getRejectError = function(error) {
-    if (
-        typeof error == 'number' &&
-        dop.core.error.reject_remote[error] !== undefined
-    ) {
-        var args = Array.prototype.slice.call(arguments, 1)
-        args.unshift(dop.core.error.reject_remote[error])
-        return dop.util.sprintf.apply(this, args)
-    }
-    return error
-}
diff --git a/src/core/protocol/localProcedureCall.js b/src/core/protocol/localProcedureCall.js
deleted file mode 100644
index 8bcb4f2..0000000
--- a/src/core/protocol/localProcedureCall.js
+++ /dev/null
@@ -1,39 +0,0 @@
-dop.core.localProcedureCall = function(
-    f,
-    args,
-    resolve,
-    reject,
-    configureReq,
-    scope
-) {
-    var req = dop.core.createAsync()
-
-    if (isFunction(configureReq)) req = configureReq(req)
-
-    req.then(resolve).catch(reject)
-    args.push(req)
-
-    try {
-        var output = f.apply(scope || req, args)
-        if (output !== req) {
-            output instanceof Promise
-                ? output.then(req.resolve).catch(req.reject)
-                : req.resolve(output)
-        }
-    } catch (e) {
-        // https://airbrake.io/blog/nodejs-error-handling/nodejs-error-class-hierarchy
-        if (
-            e instanceof Error ||
-            (typeof AssertionError == 'function' &&
-                e instanceof AssertionError) ||
-            (typeof RangeError == 'function' && e instanceof RangeError) ||
-            (typeof ReferenceError == 'function' &&
-                e instanceof ReferenceError) ||
-            (typeof SyntaxError == 'function' && e instanceof SyntaxError) ||
-            (typeof TypeError == 'function' && e instanceof TypeError)
-        ) {
-            throw e
-        }
-        req.reject(e)
-    }
-}
diff --git a/src/core/protocol/multiEncode.js b/src/core/protocol/multiEncode.js
deleted file mode 100644
index 9bcb8be..0000000
--- a/src/core/protocol/multiEncode.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// dop.core.multiEncode = function() {
-//     var encoders = arguments,
-//         length = encoders.length, v;
-//     return function recursion(property, value, index) {
-//         if (index>=length)
-//             return value;
-//         else if (index === undefined) {
-//             v = value;
-//             index = 0;
-//         }
-//         v = encoders[index](property, value);
-//         return (v!==value) ? v : recursion(property, value, index+1);
-//     }
-// };
diff --git a/src/core/protocol/onMessage.js b/src/core/protocol/onMessage.js
deleted file mode 100644
index e1598a9..0000000
--- a/src/core/protocol/onMessage.js
+++ /dev/null
@@ -1,114 +0,0 @@
-dop.core.onMessage = function(node, message_string) {
-    var messages
-    // Parsing messages
-    if (typeof message_string == 'string' && message_string[0] == '[') {
-        // https://jsperf.com/slice-substr-substring-test
-        try {
-            messages = dop.decode(message_string)
-        } catch (e) {
-            /*console.log(e);*/
-        }
-    } else messages = message_string
-
-    // Managing protocol
-    if (isArray(messages)) {
-        // Detecting if is multimessage
-        if (typeof messages[0] == 'number') messages = [messages]
-
-        // Managing all messages one by one
-        for (
-            var i = 0,
-                t = messages.length,
-                message,
-                requests,
-                request,
-                request_id,
-                response,
-                instruction_type,
-                message_typeof;
-            i < t;
-            i++
-        ) {
-            message = messages[i]
-            request_id = message[0]
-
-            // If is a number we manage the request
-            if (typeof request_id == 'number' && request_id !== 0) {
-                // If is only one request
-                message_typeof = dop.util.typeof(message[1])
-                requests =
-                    (message_typeof == 'number' && message_typeof != 'array') ||
-                    request_id < 0
-                        ? [request_id, message.slice(1)]
-                        : (requests = message)
-
-                for (
-                    var j = 1, t2 = requests.length, instruction_function;
-                    j < t2;
-                    ++j
-                ) {
-                    request = requests[j]
-
-                    if (
-                        dop.util.typeof(request) == 'array' &&
-                        ((typeof request[0] == 'number' && request_id > 0) ||
-                            request_id < 0)
-                    ) {
-                        instruction_type = request[0]
-                        instruction_function =
-                            'on' + dop.protocol.instructions[instruction_type]
-
-                        // REQUEST ===============================================================
-                        if (
-                            request_id > 0 &&
-                            isFunction(dop.protocol[instruction_function])
-                        )
-                            dop.protocol[instruction_function](
-                                node,
-                                request_id,
-                                request
-                            )
-                        // RESPONSE ===============================================================
-                        else {
-                            request_id *= -1
-
-                            if (isObject(node.requests[request_id])) {
-                                response = request
-                                request = node.requests[request_id]
-
-                                instruction_type = request[1]
-                                instruction_function =
-                                    '_on' +
-                                    dop.protocol.instructions[instruction_type]
-
-                                if (
-                                    isFunction(
-                                        dop.protocol[instruction_function]
-                                    )
-                                )
-                                    dop.protocol[instruction_function](
-                                        node,
-                                        request_id,
-                                        request,
-                                        response
-                                    )
-
-                                dop.core.deleteRequest(node, request_id)
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    // var messages,
-    //     user = (socket[dop.cons.TOKEN] === undefined) ?
-    //         socket
-    //     :
-    //         node.users[ socket[dop.cons.TOKEN] ];
-
-    // // Managing OSP protocol
-    // if (dop.util.typeof(messages) == 'array')
-    //     dop.core.manage.call(this, user, messages);
-}
diff --git a/src/core/protocol/registerNode.js b/src/core/protocol/registerNode.js
deleted file mode 100644
index f6ca311..0000000
--- a/src/core/protocol/registerNode.js
+++ /dev/null
@@ -1,9 +0,0 @@
-dop.core.registerNode = function(node) {
-    // Generating token
-    do {
-        node.token = dop.util.uuid(dop.cons.TOKEN_LENGTH)
-    } while (typeof dop.data.node[node.token] == 'object')
-    dop.data.node[node.token] = node
-    // node.token = '_' + dop.env + (dop.inc = (dop.inc || 0) + 1) + '_'
-    // node.token_local = dop.util.uuid(dop.cons.TOKEN_LENGTH / 2) // Pre token is used to combine with the remote pre token to generate the real token
-}
diff --git a/src/core/protocol/registerOwner.js b/src/core/protocol/registerOwner.js
deleted file mode 100644
index 0364c65..0000000
--- a/src/core/protocol/registerOwner.js
+++ /dev/null
@@ -1,7 +0,0 @@
-dop.core.registerOwner = function(node, object, object_owner_id) {
-    node.owner[object_owner_id] = {
-        object: object,
-        applied_version: 0, // last patch version applied correctly
-        applied: {}
-    }
-}
diff --git a/src/core/protocol/registerSubscriber.js b/src/core/protocol/registerSubscriber.js
deleted file mode 100644
index 770c52c..0000000
--- a/src/core/protocol/registerSubscriber.js
+++ /dev/null
@@ -1,33 +0,0 @@
-dop.core.registerSubscriber = function(node, object, object_path_id) {
-    var observer = dop.createObserver(function(
-        mutations,
-        filterMutationsToNode
-    ) {
-        if (typeof filterMutationsToNode == 'function')
-            mutations = filterMutationsToNode(mutations, node)
-
-        var object_id,
-            chunks,
-            patch = dop.core.getPatch(mutations)
-        for (object_id in patch) {
-            chunks = patch[object_id].chunks
-            dop.protocol.patch(node, object_path_id, chunks, subscriber)
-            return
-        }
-    })
-    var subscriber = {
-        observer: observer,
-        object: object,
-        version: 0, // incremental integer for new patches
-        pending: []
-    }
-    observer.observeAll(object)
-    node.subscriber[object_path_id] = subscriber
-}
-
-// object_data.node[node.token] = {
-//     version: 0,
-//     pending: [],
-//     applied_version: 0, // last patch version applied correctly
-//     applied: {}
-// }
diff --git a/src/core/protocol/sendRequest.js b/src/core/protocol/sendRequest.js
deleted file mode 100644
index 1efda7a..0000000
--- a/src/core/protocol/sendRequest.js
+++ /dev/null
@@ -1,40 +0,0 @@
-dop.core.sendRequests = function(node) {
-    var request_queue = node.request_queue
-    var total = request_queue.length
-    if (total) {
-        var index = 0
-        var requests_wrapped = []
-        var request_string
-        var request
-        var request_id
-
-        for (; index < total; ++index) {
-            request = request_queue[index][0]
-            requests_wrapped.push(request_queue[index][1](request))
-            request_id = request[0]
-            // If is a request (not a response) we set a timeout
-            if (request_id > 0) {
-                var nameinstruction = dop.protocol.instructions[request[1]]
-                request.timeout = setTimeout(function() {
-                    // if (node.requests[request_id] !== undefined) {
-                    dop.protocol['on' + nameinstruction + 'timeout'](
-                        node,
-                        request_id,
-                        request
-                    )
-                    delete node.requests[request_id]
-                    // }
-                }, dop.protocol.timeouts[nameinstruction])
-            }
-        }
-
-        // If we have more than 1 request we wrap it into one
-        request_string =
-            index > 1
-                ? '[' + requests_wrapped.join(',') + ']'
-                : requests_wrapped[0]
-
-        node.request_queue = []
-        node.send(request_string)
-    }
-}
diff --git a/src/core/protocol/storeAndSendRequests.js b/src/core/protocol/storeAndSendRequests.js
deleted file mode 100644
index 441c721..0000000
--- a/src/core/protocol/storeAndSendRequests.js
+++ /dev/null
@@ -1,4 +0,0 @@
-dop.core.storeAndSendRequests = function(node, request, wrapper) {
-    dop.core.storeRequest(node, request, wrapper)
-    dop.core.sendRequests(node)
-}
diff --git a/src/core/protocol/storeRequest.js b/src/core/protocol/storeRequest.js
deleted file mode 100644
index 9f6936a..0000000
--- a/src/core/protocol/storeRequest.js
+++ /dev/null
@@ -1,4 +0,0 @@
-dop.core.storeRequest = function(node, request, wrapper) {
-    if (typeof wrapper != 'function') wrapper = dop.encode
-    node.request_queue.push([request, wrapper])
-}
diff --git a/src/core/protocol/unregisterNode.js b/src/core/protocol/unregisterNode.js
deleted file mode 100644
index efacfd3..0000000
--- a/src/core/protocol/unregisterNode.js
+++ /dev/null
@@ -1,5 +0,0 @@
-dop.core.unregisterNode = function(node) {
-    delete dop.data.node[node.token]
-    for (var object_path_id in node.subscriber)
-        node.subscriber[object_path_id].observer.destroy()
-}
diff --git a/src/dop.js b/src/dop.js
deleted file mode 100644
index e6b38ce..0000000
--- a/src/dop.js
+++ /dev/null
@@ -1,63 +0,0 @@
-(function factory(root) {
-
-var dop = {
-    name: 'dop',
-    version: '{{VERSION}}',
-    create: factory,
-
-    // Internal data
-    data: {
-        node_inc: 0,
-        node: {},
-
-        object_inc: 1,
-        // object: {},
-
-        collectors: [],
-
-        gets_collecting: false,
-        gets_paths: [],
-
-        computed_inc: 0,
-        computed: {},
-
-        observers_inc: 0,
-        observers: {},
-
-        path: {
-            // "1.thepath.value": {
-                // observers_object: {},
-                // observers_prop: {},
-                // observers_all: {},
-                // interceptors: {},
-                // interceptors_prop: {},
-                // computeds: [],
-                // derivations: [],
-            // }
-        }
-    },
-    
-    // src
-    util: {},
-    core: {},
-    protocol: {},
-    transports: {listen:{}, connect:{}},
-
-    // Constants
-    cons: {
-        DOP: '~DOP',
-        TOKEN_LENGTH: 32,        
-        REMOTE_FUNCTION: '$DOP_REMOTE_FUNCTION',
-        REMOTE_FUNCTION_UNSETUP: '$DOP_REMOTE_FUNCTION_UNSETUP',
-        BROADCAST_FUNCTION: '$DOP_BROADCAST_FUNCTION',
-        COMPUTED_FUNCTION: '$DOP_COMPUTED_FUNCTION',
-
-        // NODE_STATUS_CONNECTED: 'CONNECTED', // CONNECTED
-        // NODE_STATUS_DISCONNECTED: 'DISCONNECTED', // DISCONNECTED, can't be reconnected
-
-        ON_CONNECT: 'connect',
-        ON_MESSAGE: 'message',
-        ON_DISCONNECT: 'disconnect',
-    }
-
-}; 
\ No newline at end of file
diff --git a/src/env/browser/emitter.js b/src/env/browser/emitter.js
deleted file mode 100644
index 7c320de..0000000
--- a/src/env/browser/emitter.js
+++ /dev/null
@@ -1,71 +0,0 @@
-dop.util.emitter = function() {
-    this._events = {}
-}
-
-dop.util.emitter.prototype.on = function(name, callback, once) {
-    if (isFunction(callback)) {
-        if (!isObject(this._events)) this._events = {}
-        if (!isObject(this._events[name])) this._events[name] = []
-        this._events[name].push(once === true ? [callback, true] : [callback])
-    }
-    return this
-}
-
-dop.util.emitter.prototype.once = function(name, callback) {
-    return this.on(name, callback, true)
-}
-
-dop.util.emitter.prototype.emit = function(name) {
-    if (isObject(this._events[name]) && this._events[name].length > 0) {
-        for (
-            var i = 0,
-                fun = [],
-                args = Array.prototype.slice.call(arguments, 1);
-            i < this._events[name].length;
-            i++
-        ) {
-            fun.push(this._events[name][i][0])
-            if (this._events[name][i][1] === true) {
-                this._events[name].splice(i, 1)
-                i -= 1
-            }
-        }
-        for (i = 0; i < fun.length; i++) fun[i].apply(this, args)
-    }
-    return this
-}
-
-dop.util.emitter.prototype.removeListener = function(name, callback) {
-    if (isObject(this._events[name]) && this._events[name].length > 0) {
-        for (var i = 0; i < this._events[name].length; i++) {
-            if (this._events[name][i][0] === callback) {
-                this._events[name].splice(i, 1)
-                i -= 1
-            }
-        }
-    }
-    return this
-}
-
-/*
-name = 'T@!#asty ';
-emitter = new require('events').EventEmitter();
-emitter = new dop.util.emitter();
-
-emitter.on(name, function() {
-    console.log('AAA', arguments.length); 
-})
-
-cached = function() { console.log('BBB',this._events[name].length); emitter.removeListener(name, cached) };
-emitter.on(name, cached);
-emitter.on(name, cached);
-
-emitter.once(name, function() {
-    console.log('CCC', this._events[name].length); 
-})
-
-
-emitter.emit(name);
-emitter.emit(name, 2, 3);
-emitter.emit(name, 4);
-*/
diff --git a/src/env/browser/getDefaultConnectTransport.js b/src/env/browser/getDefaultConnectTransport.js
deleted file mode 100644
index 6fe31c3..0000000
--- a/src/env/browser/getDefaultConnectTransport.js
+++ /dev/null
@@ -1,3 +0,0 @@
-dop.getDefaultConnectTransport = function() {
-    return dop.transports.connect.websocket
-}
diff --git a/src/env/browser/getDefaultListenTransport.js b/src/env/browser/getDefaultListenTransport.js
deleted file mode 100644
index 0d94f41..0000000
--- a/src/env/browser/getDefaultListenTransport.js
+++ /dev/null
@@ -1,3 +0,0 @@
-dop.getDefaultListenTransport = function() {
-    return dop.transports.listen.local
-}
diff --git a/src/env/browser/websocket.js b/src/env/browser/websocket.js
deleted file mode 100644
index e8b55c2..0000000
--- a/src/env/browser/websocket.js
+++ /dev/null
@@ -1,78 +0,0 @@
-;(function(root) {
-    function websocket(dop, options) {
-        var url = 'ws://localhost:4444/' + dop.name
-        if (typeof options.url == 'string') {
-            url = options.url.replace('http', 'ws')
-        } else if (
-            typeof window != 'undefined' &&
-            typeof window.location != 'undefined' &&
-            /http/.test(window.location.href)
-        ) {
-            var domain_prefix = /(ss|ps)?:\/\/([^\/]+)\/?(.+)?/.exec(
-                    window.location.href
-                ),
-                protocol = domain_prefix[1] ? 'wss' : 'ws'
-            url =
-                protocol +
-                '://' +
-                domain_prefix[2].toLocaleLowerCase() +
-                '/' +
-                dop.name
-        }
-
-        var transport = dop.createTransport()
-        var WebSocket = options.transport.getApi()
-        ;(function reconnect() {
-            var socket = new WebSocket(url)
-            function send(message) {
-                if (socket.readyState === 1) {
-                    socket.send(message)
-                    return true
-                }
-                return false
-            }
-            function disconnect() {
-                socket.close()
-            }
-            var node = transport.onCreate(socket, send, disconnect)
-            socket.addEventListener('open', function() {
-                transport.onConnect(node)
-            })
-            socket.addEventListener('message', function(message) {
-                transport.onMessage(node, message.data)
-            })
-            socket.addEventListener('close', function() {
-                transport.onDisconnect(node)
-            })
-            socket.addEventListener('error', function(error) {
-                transport.onError(node, error)
-            })
-        })()
-
-        return transport
-    }
-
-    websocket.getApi = function() {
-        return window.WebSocket
-    }
-
-    // UMD
-    if (
-        typeof module == 'object' &&
-        module.exports &&
-        !(
-            typeof dop == 'object' &&
-            typeof factory == 'function' &&
-            dop.create === factory
-        ) // this is true if we are inside of dop.factory
-    ) {
-        module.exports = websocket
-    } else {
-        websocket.getApi = function() {
-            return window.WebSocket
-        }
-        typeof dop != 'undefined'
-            ? (dop.transports.connect.websocket = websocket)
-            : (root.dopTransportsConnectWebsocket = websocket)
-    }
-})(this)
diff --git a/src/env/nodejs/emitter.js b/src/env/nodejs/emitter.js
deleted file mode 100644
index 81e985c..0000000
--- a/src/env/nodejs/emitter.js
+++ /dev/null
@@ -1 +0,0 @@
-dop.util.emitter = require('events').EventEmitter
diff --git a/src/env/nodejs/getDefaultConnectTransport.js b/src/env/nodejs/getDefaultConnectTransport.js
deleted file mode 100644
index 3150cc2..0000000
--- a/src/env/nodejs/getDefaultConnectTransport.js
+++ /dev/null
@@ -1,3 +0,0 @@
-dop.getDefaultConnectTransport = function() {
-    return require('dop-transports').connect.websocket
-}
diff --git a/src/env/nodejs/getDefaultListenTransport.js b/src/env/nodejs/getDefaultListenTransport.js
deleted file mode 100644
index 98596be..0000000
--- a/src/env/nodejs/getDefaultListenTransport.js
+++ /dev/null
@@ -1,3 +0,0 @@
-dop.getDefaultListenTransport = function() {
-    return require('dop-transports').listen.websocket
-}
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..c14d793
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,84 @@
+// import { version } from '../package.json.js'
+import { isFunction } from './util/is.js'
+import { merge, converter } from './util/merge.js'
+import { producePatch } from './util/patches.js'
+
+import createNodeFactory from './api/createNodeFactory.js'
+import createStoreFactory from './api/createStoreFactory.js'
+import applyPatchFactory from './api/applyPatchFactory.js'
+
+import Primitives from './types/Primitives.js'
+import Rpc from './types/Rpc.js'
+import Delete from './types/Delete.js'
+import Replace from './types/Replace.js'
+import Splice from './types/Splice.js'
+import Swap from './types/Swap.js'
+import Multi from './types/Multi.js'
+
+// Deno does not support json imports: https://stackoverflow.com/questions/61907155/deno-import-json-file-as-module
+const version = '1.5.4'
+
+function factory() {
+    const patchers = []
+    const encoders = []
+    const decoders = []
+    const encode = (object, params = {}, list = encoders) =>
+        converter(object, merge(params, { encode }), list)
+    const decode = (object, params = {}, list = decoders) =>
+        converter(object, merge(params, { decode }), list)
+    const applyPatch = applyPatchFactory(patchers)
+    const createStore = createStoreFactory(applyPatch)
+    const createNode = createNodeFactory({ encode, decode })
+    const addType = ({ patch, encode, decode }) => {
+        if (isFunction(patch)) patchers.push(patch)
+        if (isFunction(encode)) encoders.push(encode)
+        if (isFunction(decode)) decoders.push(decode)
+    }
+
+    addType(Primitives)
+    addType(Rpc)
+    addType(Delete)
+    addType(Replace)
+    addType(Splice)
+    addType(Swap)
+    addType(Multi)
+
+    return {
+        version,
+        factory,
+        merge,
+        encode,
+        decode,
+        producePatch,
+        applyPatch,
+        createNode,
+        createStore,
+        addType,
+        TYPE: {
+            Delete,
+            Replace,
+            Splice,
+            Swap,
+            Multi,
+        },
+    }
+}
+
+const dop = factory()
+const { encode, decode, applyPatch, createNode, createStore, addType, TYPE } =
+    dop
+
+export default dop
+export {
+    version,
+    factory,
+    merge,
+    encode,
+    decode,
+    producePatch,
+    applyPatch,
+    createNode,
+    createStore,
+    addType,
+    TYPE,
+}
diff --git a/src/protocol/_oncall.js b/src/protocol/_oncall.js
deleted file mode 100644
index fa2d4f8..0000000
--- a/src/protocol/_oncall.js
+++ /dev/null
@@ -1,10 +0,0 @@
-dop.protocol._oncall = function(node, request_id, request, response) {
-    var rejection = response[0],
-        promise = request.promise
-    if (rejection !== undefined) {
-        if (rejection === 0) promise.resolve(response[1])
-        else if (rejection === dop.core.error.reject_remote.CUSTOM_REJECTION)
-            promise.reject(response[1])
-        else promise.reject(dop.core.getRejectError(rejection))
-    }
-}
diff --git a/src/protocol/_onpatch.js b/src/protocol/_onpatch.js
deleted file mode 100644
index 707ba1c..0000000
--- a/src/protocol/_onpatch.js
+++ /dev/null
@@ -1,33 +0,0 @@
-dop.protocol._onpatch = function(node, request_id, request, response) {
-    var rejection = response[0],
-        object_id_owner = request[2],
-        subscriber = node.subscriber[object_id_owner],
-        version = request[3],
-        pending_list = subscriber.pending,
-        promise = request.promise,
-        index = 0,
-        total = pending_list.length,
-        version_item
-
-    if (rejection !== undefined) {
-        if (rejection === 0) {
-            for (; index < total; index++) {
-                version_item = pending_list[index][0]
-                // Removing from pending because its been received correctly
-                if (version_item >= version) {
-                    if (version_item === version) pending_list.splice(index, 1)
-                    break
-                }
-                // Resending
-                else
-                    dop.protocol.patchSend(
-                        node,
-                        object_id_owner,
-                        version_item,
-                        pending_list[index][1]
-                    )
-            }
-            promise.resolve(response[1])
-        } else promise.reject(dop.core.getRejectError(rejection))
-    }
-}
diff --git a/src/protocol/_onsubscribe.js b/src/protocol/_onsubscribe.js
deleted file mode 100644
index 87ca4d5..0000000
--- a/src/protocol/_onsubscribe.js
+++ /dev/null
@@ -1,50 +0,0 @@
-dop.protocol._onsubscribe = function(node, request_id, request, response) {
-    if (response[0] !== undefined) {
-        if (response[0] !== 0)
-            request.promise.reject(dop.core.getRejectError(response[0]))
-        else {
-            var object_owner_id = response[1]
-            var object_owner = response[2]
-            var object
-
-            // Already subscribed
-            if (node.owner[object_owner_id] !== undefined) {
-                object = node.owner[object_owner_id].object
-            }
-            // New subscription
-            else {
-                var collector = dop.collect()
-                // New object
-                if (request.into === undefined) {
-                    object = dop.register(object_owner)
-                }
-                // Registered object
-                else if (dop.isRegistered(request.into)) {
-                    object = dop.core.mergeSubscription(
-                        request.into,
-                        object_owner
-                    )
-                }
-                // No registered object
-                else {
-                    object = dop.core.mergeSubscription(
-                        dop.register(request.into),
-                        object_owner
-                    )
-                }
-                dop.core.configureRemoteFunctions(node, object, object_owner_id)
-                dop.core.registerOwner(node, object, object_owner_id)
-                collector.emit()
-            }
-
-            // Resolving/Rejecting promise
-            if (!isObject(object)) {
-                request.promise.reject(
-                    dop.core.error.reject_local.OBJECT_NOT_FOUND
-                )
-            } else {
-                request.promise.resolve(dop.getObjectProxy(object))
-            }
-        }
-    }
-}
diff --git a/src/protocol/_onunsubscribe.js b/src/protocol/_onunsubscribe.js
deleted file mode 100644
index 23d0f25..0000000
--- a/src/protocol/_onunsubscribe.js
+++ /dev/null
@@ -1,35 +0,0 @@
-dop.protocol._onunsubscribe = function(node, request_id, request, response) {
-    if (response[0] !== undefined) {
-        if (response[0] !== 0) request.promise.reject(response[0])
-        else {
-            var object_owner_id = request[2],
-                object_id = node.owner[object_owner_id],
-                object_data = dop.data.object[object_id]
-
-            if (
-                isObject(object_data) &&
-                isObject(object_data.node[node.token]) &&
-                object_data.node[node.token].owner === object_owner_id
-            ) {
-                var roles = object_data.node[node.token]
-                roles.owner = 0
-
-                if (roles.subscriber === 0) object_data.nodes_total -= 1
-
-                if (object_data.nodes_total === 0) {
-                    // Deleting object
-                    delete dop.data.object[object_id]
-                    // Deleting reference of the object in the node instance
-                    for (var id in node.owner) {
-                        if (node.owner[id] === object_id) {
-                            delete node.owner[id]
-                            break
-                        }
-                    }
-                }
-
-                request.promise.resolve()
-            }
-        }
-    }
-}
diff --git a/src/protocol/call.js b/src/protocol/call.js
deleted file mode 100644
index 5878742..0000000
--- a/src/protocol/call.js
+++ /dev/null
@@ -1,15 +0,0 @@
-dop.protocol.call = function(node, object_owner_id, path, params) {
-    var owner = node.owner[object_owner_id]
-    if (isObject(owner)) {
-        params = Array.prototype.slice.call(params, 0)
-        var request = dop.core.createRequest(
-            node,
-            dop.protocol.instructions.call,
-            object_owner_id,
-            path,
-            params
-        )
-        dop.core.storeAndSendRequests(node, request)
-        return request.promise
-    } else return Promise.reject(dop.core.error.reject_local.NODE_NOT_FOUND)
-}
diff --git a/src/protocol/instructions.js b/src/protocol/instructions.js
deleted file mode 100644
index 843adaf..0000000
--- a/src/protocol/instructions.js
+++ /dev/null
@@ -1,52 +0,0 @@
-dop.protocol.instructions = {
-    // [<request_id>, <instruction>, <params...>]
-    // If <request_id> it's greater than 0 is a request, if is less than 0 then is the response of the request.
-
-    // Is possible send multiple requests in one message, just wrapping it in an Array. But the order of the responses is not determined. Which means the response of request_idTwo could be resolved before request_idOne
-    // [[<request_id1>, <instruction>, <params...>], [<request_id2>, <instruction>, <params...>]]
-
-    // Is possible send one request with multiple instructions. The response will be recieved when all the requests are resolved. The response could be only one. But if the response is multiple has to respect the order
-    // [<request_id>, [<instruction>, <params...>], [<instruction>, <params...>]]
-
-    // If the response has a 0 as second parameter, means the response it's fulfilled. Any other value is an error
-    // [-1234, 0, <params...>]
-
-    // Also the error response could be custom as string
-    // [-1234, 'My custom message error']
-
-    // Response with instructions, if the second parameter of the response is an array it means is an instruction that could be (set, delete or merge)
-    // [-<request_id>, [<instruction>, <params...>], [<instruction>, <params...>]]
-
-    // Sending the same request without parameters means a cancel/abort of the request
-    // [1234]
-
-    // Subscriber -> Owner
-    subscribe: 1, // [ <request_id>, <instruction>, <params...>]
-    // [-<request_id>, 0, <object_id>, <data_object>]
-    // [-<request_id>, 0, <object_id>, ['path']]
-
-    // Subscriber -> Owner
-    unsubscribe: 2, // [ <request_id>, <instruction>, <object_id>]
-    // [-<request_id>, 0]
-
-    // Subscriber -> Owner
-    call: 3, // [ <request_id>, <instruction>, <object_id>, ['path','subpath'], [<params...>]]
-    // [-<request_id>, 0, <return>]
-
-    // Owner -> Subscriber
-    broadcast: 4, // [ <request_id>, <instruction>, <object_id>, ['path','subpath'], [<params...>]]
-    // [-<request_id>, 0, <return>]
-
-    // Owner -> Subscriber
-    patch: 5, // [ <request_id>, <instruction>, <object_id>, <version>, <patch>]
-    // [-<request_id>, 0]
-
-    1: 'subscribe',
-    2: 'unsubscribe',
-    3: 'call',
-    4: 'broadcast',
-    5: 'patch'
-}
-
-// for (var instruction in dop.protocol.instructions)
-// dop.protocol.instructions[ dop.protocol.instructions[instruction] ] = instruction;
diff --git a/src/protocol/instructionsPatchs.js b/src/protocol/instructionsPatchs.js
deleted file mode 100644
index 13498a9..0000000
--- a/src/protocol/instructionsPatchs.js
+++ /dev/null
@@ -1,44 +0,0 @@
-dop.protocol.instructionsPatchs = {
-    delete: 0, // Delete
-    object: 1, // New object or array
-    splice: 2, // Splice array
-    swaps: 3, // Swap array
-    function: '~F', // Remote function
-
-    // No standard, only for JavaScript
-    undefined: '~U',
-    nan: '~N',
-    regex: '~R',
-    infinity: '~I',
-    _infinity: '~i'
-}
-
-// a={
-//     a: [0],
-//     b: undefined,
-//     c: "[0]",
-//     newarr: {a:[0],b:undefined,c:"[0]",d:[4,[0],{}]},
-// }
-
-// // --------
-
-// b={
-//     a: [2,[0]],
-//     b: [0],
-//     c: "[0]",
-//     newarr: [2,{a:[2,[0]],b:[0],c:"[0]",d:[2,[4,[2,[0]],[2,{}]]] }],
-// }
-
-// c={
-//     a: [2,[0]],
-//     b: [0],
-//     c: "[0]",
-//     newarr: [2,{a:"[0]",b:[0],c:"[[0]",d:[4,[0],{}]}],
-// }
-
-// c={
-//     a: [2,[0]],
-//     b: "~U",
-//     c: "[0]",
-//     newarr: [2,{a:[0],b:"~U",c:"[0]",d:[4,[0],{}]}],
-// }
diff --git a/src/protocol/oncall.js b/src/protocol/oncall.js
deleted file mode 100644
index 1917145..0000000
--- a/src/protocol/oncall.js
+++ /dev/null
@@ -1,58 +0,0 @@
-dop.protocol.oncall = function(node, request_id, request) {
-    var object_id = request[1],
-        path = request[2],
-        params = request[3],
-        subscriber = node.subscriber[object_id]
-
-    if (isObject(subscriber)) {
-        var function_name = path.pop(),
-            object = dop.util.get(subscriber.object, path),
-            f = object[function_name]
-
-        if (isFunction(f)) {
-            function resolve(value) {
-                var response = dop.core.createResponse(request_id, 0)
-                if (value !== undefined) response.push(value)
-                dop.core.storeAndSendRequests(node, response)
-                return value
-            }
-            function reject(err) {
-                dop.core.storeAndSendRequests(
-                    node,
-                    dop.core.createResponse(
-                        request_id,
-                        dop.core.error.reject_remote.CUSTOM_REJECTION,
-                        err
-                    )
-                )
-            }
-
-            if (dop.isRemoteFunction(f))
-                f.apply(null, params)
-                    .then(resolve)
-                    .catch(reject)
-            else
-                dop.core.localProcedureCall(
-                    f,
-                    params,
-                    resolve,
-                    reject,
-                    function(req) {
-                        req.node = node
-                        return req
-                    },
-                    dop.getObjectProxy(object)
-                )
-
-            return
-        }
-    }
-
-    dop.core.storeAndSendRequests(
-        node,
-        dop.core.createResponse(
-            request_id,
-            dop.core.error.reject_remote.FUNCTION_NOT_FOUND
-        )
-    )
-}
diff --git a/src/protocol/onpatch.js b/src/protocol/onpatch.js
deleted file mode 100644
index 69b1f57..0000000
--- a/src/protocol/onpatch.js
+++ /dev/null
@@ -1,34 +0,0 @@
-dop.protocol.onpatch = function(node, request_id, request) {
-    var object_id_owner = request[1],
-        version = request[2],
-        patch = request[3],
-        owner = node.owner[object_id_owner],
-        response = dop.core.createResponse(request_id),
-        collector
-
-    if (owner !== undefined) {
-        // Storing patch
-        if (
-            owner.applied_version < version &&
-            owner.applied[version] === undefined
-        ) {
-            // Storing patch
-            owner.applied[version] = patch
-            // Applying
-            collector = dop.collect()
-            while (owner.applied[owner.applied_version + 1]) {
-                owner.applied_version += 1
-                dop.core.setPatch(
-                    owner.object,
-                    owner.applied[owner.applied_version],
-                    dop.core.setPatchFunctionMutator
-                )
-                delete owner.applied[owner.applied_version]
-            }
-            collector.emit()
-        }
-        response.push(0)
-    } else response.push(dop.core.error.reject_remote.OBJECT_NOT_FOUND)
-
-    dop.core.storeAndSendRequests(node, response)
-}
diff --git a/src/protocol/onpatchtimeout.js b/src/protocol/onpatchtimeout.js
deleted file mode 100644
index 507aa48..0000000
--- a/src/protocol/onpatchtimeout.js
+++ /dev/null
@@ -1,4 +0,0 @@
-
-dop.protocol.onpatchtimeout = function(node, request_id, request) {
-    dop.protocol.patchSend(node, request[2], request[3], request[4]);
-};
\ No newline at end of file
diff --git a/src/protocol/onsubscribe.js b/src/protocol/onsubscribe.js
deleted file mode 100644
index 8dee248..0000000
--- a/src/protocol/onsubscribe.js
+++ /dev/null
@@ -1,61 +0,0 @@
-dop.protocol.onsubscribe = function(node, request_id, request) {
-    if (isFunction(dop.data.onsubscribe)) {
-        var params = Array.prototype.slice.call(request, 1)
-
-        dop.core.localProcedureCall(
-            dop.data.onsubscribe,
-            params,
-            function resolve(value) {
-                if (dop.isPojoObject(value)) {
-                    var response = dop.core.createResponse(request_id, 0)
-                    var object = dop.register(value)
-                    var object_path_id = dop.getObjectPathId(object)
-                    // New object
-                    if (node.subscriber[object_path_id] === undefined) {
-                        dop.core.registerSubscriber(
-                            node,
-                            object,
-                            object_path_id
-                        )
-                        response.push(object_path_id, object)
-                    }
-                    // Object already subscribed
-                    else {
-                        response.push(object_path_id)
-                    }
-
-                    dop.core.storeAndSendRequests(
-                        node,
-                        response,
-                        dop.encodeFunction
-                    )
-                } else if (value === undefined) {
-                    return Promise.reject(
-                        dop.core.error.reject_remote.OBJECT_NOT_FOUND
-                    )
-                }
-                // http://www.2ality.com/2016/03/promise-rejections-vs-exceptions.html
-                // http://stackoverflow.com/questions/41254636/catch-an-error-inside-of-promise-resolver
-                else {
-                    dop.util.invariant(
-                        false,
-                        'dop.onSubscribe callback must return or resolve a regular object'
-                    )
-                }
-            },
-            reject,
-            function(req) {
-                req.node = node
-                return req
-            }
-        )
-    } else {
-        reject(dop.core.error.reject_remote.OBJECT_NOT_FOUND)
-    }
-
-    function reject(error) {
-        var response = dop.core.createResponse(request_id)
-        error instanceof Error ? console.log(error.stack) : response.push(error)
-        dop.core.storeAndSendRequests(node, response, JSON.stringify)
-    }
-}
diff --git a/src/protocol/ontimeout.js b/src/protocol/ontimeout.js
deleted file mode 100644
index 4760a4d..0000000
--- a/src/protocol/ontimeout.js
+++ /dev/null
@@ -1,10 +0,0 @@
-dop.protocol.onsubscribetimeout = 
-dop.protocol.onunsubscribetimeout = 
-dop.protocol.oncalltimeout = 
-dop.protocol.onbroadcasttimeout = function(
-    node,
-    request_id,
-    request
-) {
-    request.promise.reject(dop.core.error.reject_local.TIMEOUT_REQUEST)
-}
diff --git a/src/protocol/onunsubscribe.js b/src/protocol/onunsubscribe.js
deleted file mode 100644
index 75da652..0000000
--- a/src/protocol/onunsubscribe.js
+++ /dev/null
@@ -1,22 +0,0 @@
-dop.protocol.onunsubscribe = function(node, request_id, request) {
-    var object_id = request[1],
-        object_data = dop.data.object[object_id],
-        response = dop.core.createResponse(request_id)
-
-    if (
-        isObject(object_data) &&
-        isObject(object_data.node[node.token]) &&
-        object_data.node[node.token].subscriber
-    ) {
-        var roles = object_data.node[node.token]
-        roles.subscriber = 0
-
-        if (roles.owner === 0) object_data.nodes_total -= 1
-
-        if (object_data.nodes_total === 0) delete dop.data.object[object_id]
-
-        response.push(0)
-    } else response.push(dop.core.error.reject_remote.SUBSCRIPTION_NOT_FOUND)
-
-    dop.core.storeAndSendRequests(node, response)
-}
diff --git a/src/protocol/patch.js b/src/protocol/patch.js
deleted file mode 100644
index 0618d86..0000000
--- a/src/protocol/patch.js
+++ /dev/null
@@ -1,18 +0,0 @@
-dop.protocol.patch = function(node, object_path_id, chunks, subscriber) {
-    var version = ++subscriber.version
-    subscriber.pending.push([version, chunks])
-    return dop.protocol.patchSend(node, object_path_id, version, chunks)
-}
-
-// Also used by dop.protocol._onpatch
-dop.protocol.patchSend = function(node, object_path_id, version, chunks) {
-    var request = dop.core.createRequest(
-        node,
-        dop.protocol.instructions.patch,
-        object_path_id,
-        version,
-        chunks
-    )
-    dop.core.storeAndSendRequests(node, request, dop.encodeFunction)
-    return request.promise
-}
diff --git a/src/protocol/subscribe.js b/src/protocol/subscribe.js
deleted file mode 100644
index 99a85fd..0000000
--- a/src/protocol/subscribe.js
+++ /dev/null
@@ -1,15 +0,0 @@
-dop.protocol.subscribe = function(node, params) {
-    params = Array.prototype.slice.call(params, 0)
-    params.unshift(node, dop.protocol.instructions.subscribe)
-    var request = dop.core.createRequest.apply(node, params)
-    request.promise.into = function(object) {
-        if (dop.isPojoObject(object))
-            request.into = dop.isRegistered(object)
-                ? dop.getObjectProxy(object)
-                : object
-        delete request.promise.into
-        return request.promise
-    }
-    dop.core.storeAndSendRequests(node, request)
-    return request.promise
-}
diff --git a/src/protocol/timeouts.js b/src/protocol/timeouts.js
deleted file mode 100644
index 9591d3e..0000000
--- a/src/protocol/timeouts.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Default timeouts
-dop.protocol.timeouts = {
-    subscribe: 5000,
-    unsubscribe: 5000,
-    call: 10000,
-    broadcast: 10000,
-    patch: 1000
-}
diff --git a/src/protocol/unsubscribe.js b/src/protocol/unsubscribe.js
deleted file mode 100644
index 8e55238..0000000
--- a/src/protocol/unsubscribe.js
+++ /dev/null
@@ -1,18 +0,0 @@
-dop.protocol.unsubscribe = function(node, object) {
-    var object_id = dop.getObjectId(object),
-        object_data = dop.data.object[object_id]
-
-    if (
-        isObject(object_data) &&
-        isObject(object_data.node[node.token]) &&
-        object_data.node[node.token].owner > 0
-    ) {
-        var request = dop.core.createRequest(
-            node,
-            dop.protocol.instructions.unsubscribe,
-            object_data.node[node.token].owner
-        )
-        dop.core.storeAndSendRequests(node, request)
-        return request.promise
-    } else return Promise.reject(dop.core.error.reject_remote[2])
-}
diff --git a/src/types/Delete.js b/src/types/Delete.js
new file mode 100644
index 0000000..ea87b83
--- /dev/null
+++ b/src/types/Delete.js
@@ -0,0 +1,41 @@
+import { ESCAPE_KEY, DELETE_KEY } from '../const.js'
+import { isValidToEscape } from '../util/is.js'
+import { getUniqueKey } from '../util/getset.js'
+
+export default function Delete() {
+    if (!(this instanceof Delete)) {
+        return new Delete()
+    }
+}
+
+Delete.patch = function ({ patch, target, prop, old_value }) {
+    if (patch[prop] instanceof Delete || patch[prop] === Delete) {
+        delete target[prop]
+    }
+    return old_value
+}
+
+Delete.encode = function ({ value }) {
+    if (value instanceof Delete || value === Delete) {
+        return { [DELETE_KEY]: 0 }
+    } else if (isValidToDecodeDelete({ value })) {
+        return { [ESCAPE_KEY]: value }
+    }
+    return value
+}
+
+Delete.decode = function ({ value }) {
+    if (isValidToDecodeDelete({ value })) {
+        return new Delete()
+    } else if (
+        isValidToEscape({ value }) &&
+        isValidToDecodeDelete({ value: value[ESCAPE_KEY] })
+    ) {
+        return value[ESCAPE_KEY]
+    }
+    return value
+}
+
+function isValidToDecodeDelete({ value }) {
+    return getUniqueKey(value) === DELETE_KEY && value[DELETE_KEY] === 0
+}
diff --git a/src/types/Multi.js b/src/types/Multi.js
new file mode 100644
index 0000000..90537a3
--- /dev/null
+++ b/src/types/Multi.js
@@ -0,0 +1,48 @@
+import { ESCAPE_KEY, MULTI_KEY } from '../const.js'
+import { getUniqueKey } from '../util/getset.js'
+import { isArray, isValidToEscape } from '../util/is.js'
+
+export default function Multi(...values) {
+    if (!(this instanceof Multi)) {
+        return new Multi(...values)
+    }
+    this.values = values
+}
+
+Multi.patch = function ({ patch, target, prop, old_value, applyPatch }) {
+    const patch_value = patch[prop]
+    if (patch_value instanceof Multi) {
+        target[prop] = old_value
+        const unpatch = patch_value.values.map((value) => {
+            const { unpatch } = applyPatch(target, { [prop]: value })
+            return unpatch[prop]
+        })
+        return Multi.apply(null, unpatch.reverse())
+    }
+    return old_value
+}
+
+Multi.encode = function ({ value, encode }) {
+    if (value instanceof Multi) {
+        return { [MULTI_KEY]: encode(value.values) }
+    } else if (isValidToDecodeMulti({ value })) {
+        return { [ESCAPE_KEY]: value }
+    }
+    return value
+}
+
+Multi.decode = function ({ value, decode }) {
+    if (isValidToDecodeMulti({ value })) {
+        return Multi.apply(null, decode(value[MULTI_KEY]))
+    } else if (
+        isValidToEscape({ value }) &&
+        isValidToDecodeMulti({ value: value[ESCAPE_KEY] })
+    ) {
+        return value[ESCAPE_KEY]
+    }
+    return value
+}
+
+function isValidToDecodeMulti({ value }) {
+    return getUniqueKey(value) === MULTI_KEY && isArray(value[MULTI_KEY])
+}
diff --git a/src/types/Primitives.js b/src/types/Primitives.js
new file mode 100644
index 0000000..33b2de5
--- /dev/null
+++ b/src/types/Primitives.js
@@ -0,0 +1,35 @@
+import { isArray, isPlainObject } from '../util/is.js'
+import { merge } from '../util/merge.js'
+import Replace from './Replace.js'
+import Delete from './Delete.js'
+
+export default function Primitives() {}
+
+Primitives.patch = function ({ patch, target, prop, old_value, applyPatch }) {
+    const patch_value = patch[prop]
+
+    // If target[prop] is not defined yet
+    if (!target.hasOwnProperty(prop)) {
+        old_value = new Delete()
+    }
+
+    // New array
+    if (isArray(patch_value)) {
+        target[prop] = merge([], patch_value)
+        if (isPlainObject(old_value)) {
+            old_value = Replace(old_value)
+        }
+    }
+
+    // Object as patch
+    else if (isPlainObject(patch_value)) {
+        target[prop] = applyPatch({}, patch_value).result
+    }
+
+    // Any other
+    else {
+        target[prop] = patch_value
+    }
+
+    return old_value
+}
diff --git a/src/types/Replace.js b/src/types/Replace.js
new file mode 100644
index 0000000..f8367b8
--- /dev/null
+++ b/src/types/Replace.js
@@ -0,0 +1,39 @@
+import { ESCAPE_KEY, REPLACE_KEY } from '../const.js'
+import { isValidToEscape, isValidToDecode } from '../util/is.js'
+import Delete from './Delete.js'
+
+export default function Replace(value) {
+    if (!(this instanceof Replace)) {
+        return new Replace(value)
+    }
+    this.value = value
+}
+
+Replace.patch = function ({ patch, target, prop, old_value }) {
+    if (patch[prop] instanceof Replace) {
+        target[prop] = patch[prop].value
+        return old_value instanceof Delete ? old_value : new Replace(old_value)
+    }
+    return old_value
+}
+
+Replace.encode = function ({ value, encode }) {
+    if (value instanceof Replace) {
+        return { [REPLACE_KEY]: encode(value.value) }
+    } else if (isValidToDecode({ value, key: REPLACE_KEY })) {
+        return { [ESCAPE_KEY]: value }
+    }
+    return value
+}
+
+Replace.decode = function ({ value, decode }) {
+    if (isValidToDecode({ value, key: REPLACE_KEY })) {
+        return new Replace(decode(value[REPLACE_KEY]))
+    } else if (
+        isValidToEscape({ value }) &&
+        isValidToDecode({ value: value[ESCAPE_KEY], key: REPLACE_KEY })
+    ) {
+        return value[ESCAPE_KEY]
+    }
+    return value
+}
diff --git a/src/types/Rpc.js b/src/types/Rpc.js
new file mode 100644
index 0000000..d362b6a
--- /dev/null
+++ b/src/types/Rpc.js
@@ -0,0 +1,45 @@
+import { ESCAPE_KEY, RPC_KEY } from '../const.js'
+import { getUniqueKey } from '../util/getset.js'
+import {
+    isInteger,
+    isFunction,
+    isValidToEscape,
+    isValidToDecode,
+} from '../util/is.js'
+
+export default function Rpc() {}
+
+Rpc.encode = function ({ value, local_rpcs, registerLocalRpcFromEncode }) {
+    if (isFunction(value)) {
+        const function_id = local_rpcs.has(value)
+            ? local_rpcs.get(value)
+            : registerLocalRpcFromEncode(value)
+        return { [RPC_KEY]: function_id }
+    } else if (isValidToDecode({ value, key: RPC_KEY })) {
+        return { [ESCAPE_KEY]: value }
+    }
+    return value
+}
+
+Rpc.decode = function ({
+    value,
+    createRemoteFunction,
+    path,
+    caller,
+    function_creator,
+}) {
+    if (getUniqueKey(value) === RPC_KEY && isInteger(value[RPC_KEY])) {
+        return createRemoteFunction({
+            function_id: value[RPC_KEY],
+            function_creator,
+            caller,
+            path: path.slice(2),
+        })
+    } else if (
+        isValidToEscape({ value }) &&
+        isValidToDecode({ value: value[ESCAPE_KEY], key: RPC_KEY })
+    ) {
+        return value[ESCAPE_KEY]
+    }
+    return value
+}
diff --git a/src/types/Splice.js b/src/types/Splice.js
new file mode 100644
index 0000000..7e056da
--- /dev/null
+++ b/src/types/Splice.js
@@ -0,0 +1,52 @@
+import { ESCAPE_KEY, SPLICE_KEY } from '../const.js'
+import { isArray, isValidToEscape } from '../util/is.js'
+import { getUniqueKey } from '../util/getset.js'
+
+export default function Splice(...args) {
+    if (!(this instanceof Splice)) {
+        return new Splice(...args)
+    }
+    this.args = args
+}
+
+Splice.patch = function ({ patch, target, prop, old_value }) {
+    const patch_value = patch[prop]
+    if (patch_value instanceof Splice) {
+        target[prop] = old_value
+        if (isArray(old_value)) {
+            const { args } = patch_value
+            if (args[0] < 0) {
+                args[0] = old_value.length + args[0]
+            }
+            const spliced = old_value.splice.apply(old_value, args)
+            const inverted = [args[0], args.length - 2].concat(spliced)
+            return Splice.apply(null, inverted)
+        }
+    }
+    return old_value
+}
+
+Splice.encode = function ({ value, encode }) {
+    if (value instanceof Splice) {
+        return { [SPLICE_KEY]: encode(value.args) }
+    } else if (isValidToDecode({ value, key: SPLICE_KEY })) {
+        return { [ESCAPE_KEY]: value }
+    }
+    return value
+}
+
+Splice.decode = function ({ value, decode }) {
+    if (isValidToDecode({ value, key: SPLICE_KEY })) {
+        return Splice.apply(null, decode(value[SPLICE_KEY]))
+    } else if (
+        isValidToEscape({ value }) &&
+        isValidToDecode({ value: value[ESCAPE_KEY], key: SPLICE_KEY })
+    ) {
+        return value[ESCAPE_KEY]
+    }
+    return value
+}
+
+function isValidToDecode({ value }) {
+    return getUniqueKey(value) === SPLICE_KEY && isArray(value[SPLICE_KEY])
+}
diff --git a/src/types/Swap.js b/src/types/Swap.js
new file mode 100644
index 0000000..c2e010c
--- /dev/null
+++ b/src/types/Swap.js
@@ -0,0 +1,58 @@
+import { ESCAPE_KEY, SWAP_KEY } from '../const.js'
+import { isArray, isValidToEscape } from '../util/is.js'
+import { getUniqueKey } from '../util/getset.js'
+
+export default function Swap(...args) {
+    if (!(this instanceof Swap)) {
+        return new Swap(...args)
+    }
+    this.args = args
+}
+
+Swap.patch = function ({ patch, target, prop, old_value }) {
+    const patch_value = patch[prop]
+    if (patch_value instanceof Swap) {
+        target[prop] = old_value
+        if (isArray(old_value)) {
+            const array = old_value
+            const swaps = patch_value.args
+            if (array.length > 0 && swaps.length > 1) {
+                const total = swaps.length - 1
+                for (let index = 0; index < total; index += 2) {
+                    const swap_a = swaps[index]
+                    const swap_b = swaps[index + 1]
+                    const temp_item = array[swap_a]
+                    array[swap_a] = array[swap_b]
+                    array[swap_b] = temp_item
+                }
+                return Swap.apply(null, swaps.slice(0).reverse())
+            }
+        }
+    }
+    return old_value
+}
+
+Swap.encode = function ({ value }) {
+    if (value instanceof Swap) {
+        return { [SWAP_KEY]: value.args }
+    } else if (isValidToDecode({ value, key: SWAP_KEY })) {
+        return { [ESCAPE_KEY]: value }
+    }
+    return value
+}
+
+Swap.decode = function ({ value }) {
+    if (isValidToDecode({ value, key: SWAP_KEY })) {
+        return Swap.apply(null, value[SWAP_KEY])
+    } else if (
+        isValidToEscape({ value }) &&
+        isValidToDecode({ value: value[ESCAPE_KEY], key: SWAP_KEY })
+    ) {
+        return value[ESCAPE_KEY]
+    }
+    return value
+}
+
+function isValidToDecode({ value }) {
+    return getUniqueKey(value) === SWAP_KEY && isArray(value[SWAP_KEY])
+}
diff --git a/src/umd.js b/src/umd.js
deleted file mode 100644
index beaf9ed..0000000
--- a/src/umd.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// Factory
-if (root === undefined)
-    return dop;
-
-// AMD
-if (typeof define === 'function' && define.amd)
-    define([], function() { return dop });
-
-// Node
-else if (typeof module == 'object' && module.exports)
-    module.exports = dop;
-
-// Browser
-else if (typeof window == 'object' && window)
-    window.dop = dop;
-
-else
-    root.dop = dop;
-
-})(this)
\ No newline at end of file
diff --git a/src/util/alias.js b/src/util/alias.js
deleted file mode 100644
index 0d399c9..0000000
--- a/src/util/alias.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// Private alias
-function isFunction(func) {
-    return typeof func == 'function'
-}
-
-function isObject(object) {
-    return object !== null && typeof object == 'object'
-}
-
-function isArray(array) {
-    return Array.isArray(array)
-}
-
-function isNumber(number) {
-    return typeof number == 'number'
-}
-
-function isEmptyObject(object) {
-    for (var key in object) return false
-    return true
-}
diff --git a/src/util/clone.js b/src/util/clone.js
deleted file mode 100644
index 9c86500..0000000
--- a/src/util/clone.js
+++ /dev/null
@@ -1,5 +0,0 @@
-dop.util.clone = function(value) {
-    return dop.isPojoObject(value)
-        ? dop.util.merge(isArray(value) ? [] : {}, value)
-        : value
-}
diff --git a/src/util/forEach.js b/src/util/forEach.js
new file mode 100644
index 0000000..34c667a
--- /dev/null
+++ b/src/util/forEach.js
@@ -0,0 +1,27 @@
+import { isArray, isObject } from './is.js'
+
+// https://stackoverflow.com/questions/27433075/using-a-for-each-loop-on-an-empty-array-in-javascript
+export function forEach(object, callback) {
+    if (isArray(object)) {
+        for (let prop = 0; prop < object.length; ++prop) {
+            callback(object[prop], prop)
+        }
+    } else {
+        for (const prop in object) {
+            callback(object[prop], prop)
+        }
+    }
+}
+
+export function forEachDeep(object, callback, path = []) {
+    forEach(object, (value_origin, prop) => {
+        path.push(prop)
+        if (
+            callback({ object, prop, path: path.slice(0) }) &&
+            isObject(value_origin)
+        ) {
+            forEachDeep(value_origin, callback, path)
+        }
+        path.pop()
+    })
+}
diff --git a/src/util/get.js b/src/util/get.js
deleted file mode 100644
index fa36849..0000000
--- a/src/util/get.js
+++ /dev/null
@@ -1,43 +0,0 @@
-dop.util.get = function(object, path) {
-    if (path.length === 0) return object
-
-    for (
-        var index = 0, total = path.length, tmpobject;
-        index < total;
-        index++
-    ) {
-        tmpobject = object[path[index]]
-
-        if (index + 1 < total && isObject(tmpobject)) object = tmpobject
-        else if (object.hasOwnProperty(path[index])) return tmpobject
-        else return undefined
-    }
-
-    return object[path[index]]
-}
-
-// dop.util.set = function(object, path, value) {
-
-//     if (path.length == 0)
-//         return object;
-
-//     path = path.slice(0);
-//     var obj = object, objdeep, index=0, total=path.length-1;
-
-//     for (;index<total; ++index) {
-//         objdeep = obj[path[index]];
-//         obj = (objdeep && typeof objdeep == 'object') ?
-//             objdeep
-//         :
-//             obj[path[index]] = {};
-//     }
-
-//     obj[path[index]] = value;
-
-//     return object;
-// };
-
-// /*
-// ori = {test:{hs:124}}
-// console.log( dop.util.set(ori, ['test','more'], undefined))
-// */
diff --git a/src/util/getUniqueToken.js b/src/util/getUniqueToken.js
deleted file mode 100644
index 818c160..0000000
--- a/src/util/getUniqueToken.js
+++ /dev/null
@@ -1,10 +0,0 @@
-dop.util.getUniqueToken = function(token1, token2) {
-    var t1l = token1.length
-    var t2l = token2.length
-    if (t1l < t2l) {
-        token2 = token2.substr(0, t1l)
-    } else if (t2l < t1l) {
-        token1 = token1.substr(0, t2l)
-    }
-    return token1 < token2 ? token1 + token2 : token2 + token1
-}
diff --git a/src/util/getset.js b/src/util/getset.js
new file mode 100644
index 0000000..93b5be9
--- /dev/null
+++ b/src/util/getset.js
@@ -0,0 +1,38 @@
+import { is, isPlain, isObject } from './is.js'
+import { merge } from './merge.js'
+
+export function getUniqueKey(object) {
+    let key_name
+    for (const key in object) {
+        if (key_name !== undefined) {
+            return
+        }
+        key_name = key
+    }
+    return key_name
+}
+
+export function getNewPlain(value) {
+    return isPlain(value) ? merge(is(value) == 'array' ? [] : {}, value) : value
+}
+
+export function getDeep(object, path) {
+    const [prop] = path
+    if (path.length === 0) return object
+    if (isObject(object) && object.hasOwnProperty(prop)) {
+        return getDeep(object[prop], path.slice(1))
+    }
+}
+
+export function setDeep(object, path, value) {
+    path = path.slice(0)
+    const prop = path.shift()
+    if (path.length === 0) {
+        object[prop] = value
+        return
+    }
+    if (!object.hasOwnProperty(prop)) {
+        object[prop] = {}
+    }
+    return setDeep(object[prop], path, value)
+}
diff --git a/src/util/invariant.js b/src/util/invariant.js
deleted file mode 100644
index 87176f8..0000000
--- a/src/util/invariant.js
+++ /dev/null
@@ -1,9 +0,0 @@
-dop.util.invariant = function(check) {
-    if (!check) {
-        var message = dop.util.sprintf.apply(
-            this,
-            Array.prototype.slice.call(arguments, 1)
-        )
-        throw new Error('[dop] Invariant failed: ' + message)
-    }
-}
diff --git a/src/util/is.js b/src/util/is.js
new file mode 100644
index 0000000..312c1f7
--- /dev/null
+++ b/src/util/is.js
@@ -0,0 +1,63 @@
+import { getUniqueKey } from '../util/getset.js'
+import { ESCAPE_KEY } from '../const.js'
+
+export function is(value) {
+    if (value === null) return 'null'
+    if (isArray(value)) return 'array'
+    return typeof value
+}
+
+export function isFunction(fn) {
+    return typeof fn == 'function'
+}
+
+export function isArray(array) {
+    return Array.isArray(array)
+}
+
+export function isObject(object) {
+    return object !== null && typeof object == 'object'
+}
+
+export function isPlain(object) {
+    if (!isObject(object)) return false
+    const prototype = Object.getPrototypeOf(object)
+    return prototype === Object.prototype || prototype === Array.prototype
+}
+
+export function isPlainObject(object) {
+    if (!isObject(object)) return false
+    return Object.getPrototypeOf(object) === Object.prototype
+}
+
+export function isInteger(number) {
+    return (
+        typeof number === 'number' &&
+        isFinite(number) &&
+        Math.floor(number) === number
+    )
+}
+
+export function isString(string) {
+    return typeof string === 'string'
+}
+
+// export function isProxy(object) {
+//     if (typeof Proxy !== 'function') {
+//         return false
+//     }
+//     try {
+//         object instanceof Proxy
+//         return false
+//     } catch (e) {
+//         return true
+//     }
+// }
+
+export function isValidToDecode({ value, key }) {
+    return getUniqueKey(value) === key && value.hasOwnProperty(key)
+}
+
+export function isValidToEscape({ value }) {
+    return getUniqueKey(value) === ESCAPE_KEY
+}
diff --git a/src/util/map.js b/src/util/map.js
deleted file mode 100644
index c787fc4..0000000
--- a/src/util/map.js
+++ /dev/null
@@ -1,22 +0,0 @@
-dop.util.map = function() {
-    return new Map()
-    // var map = new Map()
-    // return {
-    //     size: 0,
-    //     set: function(key, value) {
-    //         var result = map.set(key, value)
-    //         console.log('set', map.size)
-    //         this.size = map.size
-    //         return result
-    //     },
-    //     get: function(key) {
-    //         return map.get(key)
-    //     },
-    //     delete: function(key) {
-    //         var result = map.delete(key)
-    //         console.log('delete', map.size)
-    //         this.size = map.size
-    //         return result
-    //     }
-    // }
-}
diff --git a/src/util/merge.js b/src/util/merge.js
index e869497..db6c32b 100644
--- a/src/util/merge.js
+++ b/src/util/merge.js
@@ -1,26 +1,74 @@
-dop.util.merge = function(first, second) {
-    var args = arguments
-    if (args.length > 2) {
-        // Remove the first 2 arguments of the arguments and add thoose arguments as merged at the begining
-        Array.prototype.splice.call(
-            args,
-            0,
-            2,
-            dop.util.merge.call(this, first, second)
-        )
-        // Recursion
-        return dop.util.merge.apply(this, args)
-    } else {
-        dop.util.path(second, this, first, dop.util.mergeMutator)
-        if (isArray(second)) first.length = second.length
-        return first
-    }
-}
-
-dop.util.mergeMutator = function(destiny, prop, value, tof_value) {
-    if (tof_value == 'object' || tof_value == 'array')
-        !destiny.hasOwnProperty(prop)
-            ? (destiny[prop] = tof_value == 'array' ? [] : {})
-            : destiny[prop]
-    else destiny[prop] = value
-}
+import { is, isPlain, isArray } from './is.js'
+import { forEachDeep } from './forEach.js'
+import { getDeep } from './getset.js'
+
+// https://jsperf.com/merge-challenge
+export function merge(target, patch) {
+    const args = arguments
+    if (args.length > 2) {
+        // Remove the target 2 arguments of the arguments and add thoose arguments as merged at the begining
+        Array.prototype.splice.call(args, 0, 2, merge.call(this, target, patch))
+        // Recursion
+        return merge.apply(this, args)
+    } else if (patch === target) {
+        return target
+    } else {
+        mergeCore(patch, target, mergeMutator)
+        return target
+    }
+}
+
+// https://jsperf.com/dop-foreachobject
+// https://2ality.com/2019/10/shared-mutable-state.html
+export function mergeCore(patch, target_root, mutator) {
+    forEachDeep(patch, ({ object, prop, path }) => {
+        const target = getDeep(target_root, path.slice(0, path.length - 1))
+        const output = mutator({ patch: object, target, prop, path })
+        return output !== false
+    })
+}
+
+export function mergeMutator({ patch, target, prop }) {
+    const origin_value = patch[prop]
+    const destiny_value = target[prop]
+    const tof_origin = is(origin_value)
+    const tof_destiny = is(destiny_value)
+    if (isPlain(origin_value)) {
+        if (!target.hasOwnProperty(prop) || tof_origin != tof_destiny) {
+            target[prop] = tof_origin == 'array' ? [] : {}
+        }
+    } else {
+        target[prop] = origin_value
+        return false // we dont go deeper
+    }
+}
+
+export function converter(patch, params, converters) {
+    const patch_root = { '': patch } // a trick to allow top level
+    const target_root = { '': isArray(patch) ? [] : {} } // a trick to allow top level
+    mergeCore(patch_root, target_root, ({ patch, prop, target, path }) => {
+        const value = converters.reduce(
+            (value, converter) =>
+                converter(
+                    merge(
+                        {
+                            value,
+                            patch,
+                            target,
+                            prop,
+                            path,
+                        },
+                        params
+                    )
+                ),
+            patch[prop]
+        )
+        if (patch[prop] !== value) {
+            target[prop] = value
+            return false // we don't go deeper
+        } else {
+            return mergeMutator({ patch, target, prop })
+        }
+    })
+    return target_root['']
+}
diff --git a/src/util/nodes.js b/src/util/nodes.js
new file mode 100644
index 0000000..91b6efb
--- /dev/null
+++ b/src/util/nodes.js
@@ -0,0 +1,52 @@
+export function localProcedureCall(fn, req, args, errorInstances) {
+    function reject(error) {
+        // https://airbrake.io/blog/nodejs-error-handling/nodejs-error-class-hierarchy
+        for (let index = 0; index < errorInstances.length; ++index) {
+            if (error instanceof errorInstances[index]) {
+                throw error
+            }
+        }
+        req.reject(error)
+    }
+
+    try {
+        const output = fn.apply(req, args)
+        if (output !== req) {
+            output instanceof Promise
+                ? output.then(req.resolve).catch(reject)
+                : req.resolve(output)
+        }
+    } catch (error) {
+        reject(error)
+    }
+}
+
+export function createRequest() {
+    let resolve
+    let reject
+    const promise = new Promise((res, rej) => {
+        resolve = res
+        reject = rej
+    })
+    promise.resolve = resolve
+    promise.reject = reject
+    return promise
+}
+
+// function Request(executor) {
+//     let resolve
+//     let reject
+//     const promise = new Promise((res, rej) => {
+//         resolve = res
+//         reject = rej
+//         // return executor(res, rej)
+//     })
+
+//     promise.__proto__ = Request.prototype
+//     promise.resolve = resolve
+//     promise.reject = reject
+//     return promise
+// }
+
+// Request.__proto__ = Promise
+// Request.prototype.__proto__ = Promise.prototype
diff --git a/src/util/patches.js b/src/util/patches.js
new file mode 100644
index 0000000..38d1dfb
--- /dev/null
+++ b/src/util/patches.js
@@ -0,0 +1,76 @@
+import { TYPE } from '../index.js'
+import { is, isPlain, isPlainObject } from './is.js'
+import { setDeep, getDeep } from './getset.js'
+import { forEachDeep } from './forEach.js'
+
+export function producePatch(baseobject, callback) {
+    const draft = {}
+    const mutations = []
+    let storing = false
+
+    forEachDeep({ draft: baseobject }, ({ object, prop, path }) => {
+        const origin = object
+        const destiny = getDeep(draft, path.slice(0, path.length - 1))
+        const origin_value = origin[prop]
+        const destiny_value = destiny[prop]
+        const tof_origin = is(origin_value)
+        const tof_destiny = is(destiny_value)
+
+        if (
+            isPlain(origin_value) &&
+            (!destiny.hasOwnProperty(prop) || tof_origin != tof_destiny)
+        ) {
+            destiny[prop] = new Proxy(tof_origin == 'array' ? [] : {}, {
+                set: (object, prop, value) => {
+                    if (storing) {
+                        mutations.push([
+                            path.slice(1).concat(prop),
+                            isPlainObject(value) //&& isPlainObject(object[prop])
+                                ? TYPE.Replace(value)
+                                : value,
+                        ])
+                    }
+                    object[prop] = value
+                    return true
+                },
+                deleteProperty: (object, prop) => {
+                    if (storing) {
+                        mutations.push([
+                            path.slice(1).concat(prop),
+                            TYPE.Delete(),
+                        ])
+                    }
+                    delete object[prop]
+                    return true
+                },
+            })
+            return true
+        } else {
+            destiny[prop] = origin_value
+            return false // we dont go deeper
+        }
+    })
+
+    storing = true
+    callback(draft.draft)
+    storing = false
+
+    const patch = createPatchFromMutations(mutations)
+    return { patch, mutations }
+}
+
+export function createPatchFromMutations(mutations) {
+    const patch = {}
+    mutations.forEach((mutation) => setDeep(patch, mutation[0], mutation[1]))
+    return patch
+}
+
+export function createPatchAndUnpatchFromMutations(mutations, object) {
+    const patch = {}
+    const unpatch = {}
+    mutations.forEach(({ path, old_value }) => {
+        setDeep(patch, path, getDeep(object, path))
+        setDeep(unpatch, path, old_value)
+    })
+    return { patch, unpatch }
+}
diff --git a/src/util/path.js b/src/util/path.js
deleted file mode 100644
index 504ad34..0000000
--- a/src/util/path.js
+++ /dev/null
@@ -1,67 +0,0 @@
-dop.util.path = function(source, callback, destiny, mutator) {
-    var has_callback = isFunction(callback),
-        has_destiny = isObject(destiny)
-    dop.util.pathRecursive(
-        source,
-        callback,
-        destiny,
-        mutator,
-        [],
-        [],
-        has_callback,
-        has_destiny
-    )
-    return destiny
-}
-
-dop.util.pathRecursive = function(
-    source,
-    callback,
-    destiny,
-    mutator,
-    circular,
-    path,
-    has_callback,
-    has_destiny
-) {
-    var prop, value, tof_value, skip
-
-    for (prop in source) {
-        skip = false
-        value = source[prop]
-        path.push(prop)
-
-        if (has_callback)
-            skip = callback(source, prop, value, destiny, path, this)
-
-        if (skip !== true) {
-            tof_value = dop.util.typeof(value)
-
-            if (has_destiny)
-                skip = mutator(destiny, prop, value, tof_value, path)
-
-            // Objects or arrays
-            if (
-                (tof_value == 'object' || tof_value == 'array') &&
-                skip !== true &&
-                value !== source &&
-                circular.indexOf(value) == -1
-                // (has_destiny && destiny[prop] !== undefined) ||
-            ) {
-                circular.push(value)
-                dop.util.pathRecursive(
-                    value,
-                    callback,
-                    has_destiny ? destiny[prop] : undefined,
-                    mutator,
-                    circular,
-                    path,
-                    has_callback,
-                    has_destiny
-                )
-            }
-
-            path.pop()
-        }
-    }
-}
diff --git a/src/util/sprintf.js b/src/util/sprintf.js
deleted file mode 100644
index b63831f..0000000
--- a/src/util/sprintf.js
+++ /dev/null
@@ -1,20 +0,0 @@
-dop.util.sprintf = function() {
-    var s = -1,
-        result,
-        str = arguments[0],
-        array = Array.prototype.slice.call(arguments, 1)
-    return str.replace(/"/g, "'").replace(/%([0-9]+)|%s/g, function() {
-        result =
-            array[
-                arguments[1] === undefined || arguments[1] === ''
-                    ? ++s
-                    : arguments[1]
-            ]
-
-        if (result === undefined) result = arguments[0]
-
-        return result
-    })
-}
-// Usage: sprintf('Code error %s for %s', 25, 'Hi') -> "Code error 25 for Hi"
-// Usage2: sprintf('Code error %1 for %0', 25, 'Hi') -> "Code error Hi for 25"
diff --git a/src/util/swap.js b/src/util/swap.js
deleted file mode 100644
index 495d179..0000000
--- a/src/util/swap.js
+++ /dev/null
@@ -1,21 +0,0 @@
-dop.util.swap = function(array, swaps, callback) {
-    if (array.length > 0 && swaps.length > 1) {
-        var index = 0,
-            total = swaps.length - 1,
-            temp_item,
-            swapA,
-            swapB,
-            is_callback = isFunction(callback)
-
-        for (; index < total; index += 2) {
-            swapA = swaps[index]
-            swapB = swaps[index + 1]
-            temp_item = array[swapA]
-            array[swapA] = array[swapB]
-            array[swapB] = temp_item
-            if (is_callback) callback(swapA, swapB)
-        }
-    }
-
-    return array
-}
diff --git a/src/util/typeof.js b/src/util/typeof.js
deleted file mode 100644
index 1f22f12..0000000
--- a/src/util/typeof.js
+++ /dev/null
@@ -1,65 +0,0 @@
-// https://jsperf.com/typeof-with-more-types
-// dop={util:{}}
-dop.util.typeof = function(value) {
-    var s = typeof value
-    if (s == 'object') {
-        if (value) {
-            if (isArray(value)) s = 'array'
-            else if (value instanceof Date) s = 'date'
-            else if (value instanceof RegExp) s = 'regexp'
-        } else s = 'null'
-    }
-    return s
-}
-
-// dop.util.typeof2 = (function() {
-
-//     var list = {
-
-//         '[object Null]': 'null',
-//         '[object Undefined]': 'undefined',
-//         '[object Object]': 'object',
-//         '[object Function]': 'function',
-//         '[object Array]': 'array',
-//         '[object Number]': 'number',
-//         '[object String]': 'string',
-//         '[object Boolean]': 'boolean',
-//         '[object Symbol]': 'symbol',
-//         '[object RegExp]': 'regexp',
-//         '[object Date]': 'date'
-//     };
-
-//     return function(type) {
-
-//         return list[ Object.prototype.toString.call(type) ];
-
-//     };
-
-// })();
-
-// Typeof=dop.util.typeof;
-// console.log(Typeof(null));
-// console.log(Typeof(undefined));
-// console.log(Typeof({}));
-// console.log(Typeof(function(){}));
-// console.log(Typeof([]));
-// console.log(Typeof(1));
-// console.log(Typeof("s"));
-// console.log(Typeof(true));
-// console.log(Typeof(/a/));
-// console.log(Typeof(new Date()));
-// console.log(Typeof(Symbol('')));
-// console.log(Typeof(new Typeof));
-
-// Typeof(null);
-// Typeof(undefined);
-// Typeof({});
-// Typeof(function(){});
-// Typeof([]);
-// Typeof(1);
-// Typeof("s");
-// Typeof(true);
-// Typeof(/a/);
-// Typeof(new Date());
-// Typeof(Symbol(''));
-// Typeof(new Typeof);
diff --git a/src/util/uuid.js b/src/util/uuid.js
deleted file mode 100644
index 2d613c3..0000000
--- a/src/util/uuid.js
+++ /dev/null
@@ -1,24 +0,0 @@
-// https://jsperf.com/uuid-vs-pseudohash
-dop.util.uuid = function(length) {
-    // if (length === undefined) length = 32
-    var text = []
-    var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
-    var charsLength = chars.length
-
-    for (var i = 0; i < length; i++)
-        text.push(chars.charAt(Math.floor(Math.random() * charsLength)))
-
-    return text.join('')
-}
-
-// dop.util.uuid = function() {
-//     for (var i = 0, uuid = '', random; i < 32; i++) {
-//         random = (Math.random() * 16) | 0
-//         if (i === 8 || i === 12 || i === 16 || i === 20) uuid += '-'
-//         uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(
-//             16
-//         )
-//     }
-
-//     return uuid
-// }
diff --git a/test/.addType.js b/test/.addType.js
new file mode 100644
index 0000000..90e8060
--- /dev/null
+++ b/test/.addType.js
@@ -0,0 +1,35 @@
+import test from 'ava'
+import { applyPatch, encode, decode, addType } from '../src'
+import Splice from '../src/types/Splice'
+import { getNewPlain } from '../src/util/getset'
+import { isPlainObject } from '../src/util/is'
+
+// Defining a type that push elements to an array
+function Push(...elements) {
+    if (!(this instanceof Push)) {
+        return new Push(...elements)
+    }
+    this.elements = elements
+}
+Push.patch = function ({ patch, target, prop, old_value }) {
+    const originValue = patch[prop]
+    if (isArray(old_value) && originValue instanceof Push) {
+        target[prop] = old_value
+        return Push.apply(null, originValue.elements)
+    }
+    return old_value
+}
+Push.encode = function ({ value }) {
+    if (value instanceof Push) {
+        return { $push: value.elements }
+    }
+    return value
+}
+Push.decode = function ({ value }) {
+    if (getUniqueKey(value) === '$push' && isArray(value['$push'])) {
+        return Push.apply(null, value['$push'])
+    }
+    return value
+}
+
+addType(Push)
diff --git a/test/.protocol/broadcast.js b/test/.protocol/broadcast.js
deleted file mode 100644
index 3f183fe..0000000
--- a/test/.protocol/broadcast.js
+++ /dev/null
@@ -1,103 +0,0 @@
-var test = require('tape')
-var dop = require('../.proxy').create()
-var dopClient1 = require('../.proxy').create()
-var dopClient2 = require('../.proxy').create()
-var transportName = process.argv[2] || 'local'
-var transportListen = require('dop-transports').listen[transportName]
-var transportConnect = require('dop-transports').connect[transportName]
-
-dop.env = 'SERVER'
-dop.data.object_inc = 25
-dopClient1.env = 'CLIENT1'
-dopClient2.env = 'CLIENT2'
-
-test('BROADCAST TESTS', function(tt) {
-    var test = function(name, cb) {
-        tt.test(name, {}, cb)
-    }
-
-    var server = dop.listen({ transport: transportListen })
-    var client1 = dopClient1.connect({
-        transport: transportConnect,
-        listener: server
-    })
-    var client2 = dopClient2.connect({
-        transport: transportConnect,
-        listener: server
-    })
-
-    var objServer = {
-        number: 1,
-        subobject: {}
-    }
-    var objClient1 = {
-        subobject: {
-            broadcastFunction: function(a, b) {
-                return a + b
-            }
-        }
-    }
-    var objClient2 = {
-        subobject: {
-            broadcastFunction: function(a, b) {
-                return a * b
-            }
-        }
-    }
-
-    dop.register(objServer)
-    dop.setBroadcastFunction(objServer.subobject, 'broadcastFunction')
-    dop.onSubscribe(function() {
-        return objServer
-    })
-
-    tt.equal(
-        typeof objServer.subobject.broadcastFunction,
-        'function',
-        'broadcastFunction is a function'
-    )
-
-    test('BROADCASTING TO CLIENTS', function(t) {
-        client1
-            .subscribe()
-            .into(objClient1)
-            .then(function(obj) {
-                return client2.subscribe().into(objClient2)
-            })
-            .then(function(obj) {
-                var promises = objServer.subobject.broadcastFunction(2, 5)
-                t.equal(Array.isArray(promises), true, 'Promises is array')
-                t.equal(promises.length, 2, 'Promises are two promises')
-                t.equal(
-                    promises[0] instanceof Promise,
-                    true,
-                    'First promise is instanceof Promise'
-                )
-                return Promise.all(promises)
-                // .catch(function(err){
-                //     console.log( err );
-                // })
-            })
-            .then(function(values) {
-                t.equal(values[0], 7, 'First value must be 2+5=7')
-                t.equal(values[1], 10, 'Second value must be 2*5=10')
-
-                objClient1.subobject.broadcastFunction = function(a, b) {
-                    return a / b
-                }
-
-                return Promise.all(objServer.subobject.broadcastFunction(10, 2))
-            })
-            .then(function(values) {
-                t.equal(values[0], 5, 'First value must be 10/2=5')
-                t.equal(values[1], 20, 'Second value must be 10*2=20')
-                server.listener.close()
-                // client1.socket.close();
-                // client2.socket.close();
-                t.end()
-            })
-    })
-
-    // More test todo
-    tt.end()
-})
diff --git a/test/.protocol/call.js b/test/.protocol/call.js
deleted file mode 100644
index 024e648..0000000
--- a/test/.protocol/call.js
+++ /dev/null
@@ -1,307 +0,0 @@
-var test = require('tape')
-var dopServer = require('../.proxy').create()
-var dopClient = require('../.proxy').create()
-var transportName = process.argv[2] || 'local'
-var transportListen = require('dop-transports').listen[transportName]
-var transportConnect = require('dop-transports').connect[transportName]
-dopServer.env = 'SERVER'
-dopClient.env = 'CLIENT'
-
-// this code would be in node.js
-var server = dopServer.listen({ transport: transportListen })
-// this code would be in browser
-var client = dopClient.connect({
-    transport: transportConnect,
-    listener: server
-})
-
-var localFunctions = dopServer.register({
-    syncOk: function(req) {
-        return 'Ok'
-    },
-
-    syncError: function(req) {
-        throw 'Error'
-    },
-
-    syncUndefined: function(re) {
-        // return
-    },
-
-    syncReqOk: function(req) {
-        return req.resolve('Ok')
-    },
-
-    syncReqError: function(req) {
-        return req.reject('Error')
-    },
-
-    syncTimeoutOk: function(req) {
-        setTimeout(function() {
-            req.resolve('Ok')
-        }, 10)
-        return req
-    },
-
-    syncTimeoutError: function(req) {
-        setTimeout(function() {
-            req.reject('Error')
-        }, 10)
-        return req
-    },
-
-    syncPromiseOk: function(req) {
-        return Promise.resolve('Ok')
-    },
-
-    syncPromiseError: function(req) {
-        return Promise.reject('Error')
-    },
-
-    // Async
-    asyncOk: function(req) {
-        return new Promise(function(resolve, reject) {
-            resolve('Ok')
-        })
-    },
-
-    asyncError: function(req) {
-        return new Promise(function(resolve, reject) {
-            reject('Error')
-        })
-    },
-
-    asyncUndefined: function(req) {
-        return new Promise(function(resolve, reject) {
-            resolve(undefined)
-        })
-    },
-
-    asyncReqOk: function(req) {
-        return new Promise(function(resolve, reject) {
-            resolve(req.resolve('Ok'))
-        })
-    },
-
-    asyncReqError: function(req) {
-        return new Promise(function(resolve, reject) {
-            resolve(req.reject('Error'))
-        })
-    },
-
-    asyncTimeoutOk: function(req) {
-        return new Promise(function(resolve, reject) {
-            setTimeout(function() {
-                req.resolve('Ok')
-            }, 10)
-            resolve(req)
-        })
-    },
-
-    asyncTimeoutError: function(req) {
-        return new Promise(function(resolve, reject) {
-            setTimeout(function() {
-                req.reject('Error')
-            }, 10)
-            resolve(req)
-        })
-    },
-
-    asyncPromiseOk: function(req) {
-        return new Promise(function(resolve, reject) {
-            resolve(Promise.resolve('Ok'))
-        })
-    },
-
-    asyncPromiseError: function(req) {
-        return new Promise(function(resolve, reject) {
-            resolve(Promise.reject('Error'))
-        })
-    }
-})
-
-// server
-dopServer.onSubscribe(function() {
-    return localFunctions
-})
-
-var remoteFunctions
-test('Matching same values', function(t) {
-    remoteFunctions = client.subscribe().then(function(o) {
-        remoteFunctions = o
-        t.deepEqual(Object.keys(remoteFunctions), Object.keys(localFunctions))
-        t.end()
-    })
-})
-
-test('syncOk', function(t) {
-    remoteFunctions.syncOk().then(function(dataremote) {
-        var datalocal = localFunctions.syncOk()
-        t.equal(dataremote, 'Ok')
-        t.equal(dataremote, datalocal)
-        t.end()
-    })
-})
-
-test('syncError', function(t) {
-    remoteFunctions
-        .syncError()
-        .then(function(dataremote) {
-            t.equal(1, 2, 'this should not happen')
-        })
-        .catch(function(e) {
-            t.equal(e, 'Error')
-            try {
-                localFunctions.syncError()
-                t.equal(1, 2, 'this should not happen 2')
-            } catch (e) {
-                t.equal(e, 'Error')
-            }
-            t.end()
-        })
-})
-
-test('syncUndefined', function(t) {
-    remoteFunctions.syncUndefined().then(function(dataremote) {
-        var datalocal = localFunctions.syncUndefined()
-        t.equal(dataremote, undefined)
-        t.equal(dataremote, datalocal)
-        t.end()
-    })
-})
-
-test('syncReqOk', function(t) {
-    remoteFunctions.syncReqOk().then(function(dataremote) {
-        t.equal(dataremote, 'Ok')
-        t.end()
-    })
-})
-
-test('syncReqError', function(t) {
-    remoteFunctions
-        .syncReqError()
-        .then(function(dataremote) {
-            t.equal(1, 2, 'this should not happen')
-        })
-        .catch(function(e) {
-            t.equal(e, 'Error')
-            t.end()
-        })
-})
-
-test('syncTimeoutOk', function(t) {
-    remoteFunctions.syncTimeoutOk().then(function(dataremote) {
-        t.equal(dataremote, 'Ok')
-        t.end()
-    })
-})
-
-test('syncTimeoutError', function(t) {
-    remoteFunctions
-        .syncTimeoutError()
-        .then(function(dataremote) {
-            t.equal(1, 2, 'this should not happen')
-        })
-        .catch(function(e) {
-            t.equal(e, 'Error')
-            t.end()
-        })
-})
-
-test('syncPromiseOk', function(t) {
-    remoteFunctions.syncPromiseOk().then(function(dataremote) {
-        t.equal(dataremote, 'Ok')
-        t.end()
-    })
-})
-
-test('syncPromiseError', function(t) {
-    remoteFunctions
-        .syncPromiseError()
-        .then(function(dataremote) {
-            t.equal(1, 2, 'this should not happen')
-        })
-        .catch(function(e) {
-            t.equal(e, 'Error')
-            t.end()
-        })
-})
-
-test('asyncOk', function(t) {
-    remoteFunctions.asyncOk().then(function(dataremote) {
-        t.equal(dataremote, 'Ok')
-        t.end()
-    })
-})
-
-test('asyncError', function(t) {
-    remoteFunctions
-        .asyncError()
-        .then(function(dataremote) {
-            t.equal(1, 2, 'this should not happen')
-        })
-        .catch(function(e) {
-            t.equal(e, 'Error')
-            t.end()
-        })
-})
-
-test('asyncUndefined', function(t) {
-    remoteFunctions.asyncUndefined().then(function(dataremote) {
-        t.equal(dataremote, undefined)
-        t.end()
-    })
-})
-
-test('asyncReqOk', function(t) {
-    remoteFunctions.asyncReqOk().then(function(dataremote) {
-        t.equal(dataremote, 'Ok')
-        t.end()
-    })
-})
-
-test('asyncReqError', function(t) {
-    remoteFunctions
-        .asyncReqError()
-        .then(function(dataremote) {
-            t.equal(1, 2, 'this should not happen')
-        })
-        .catch(function(e) {
-            t.equal(e, 'Error')
-            t.end()
-        })
-})
-
-test('asyncTimeoutOk', function(t) {
-    remoteFunctions.asyncTimeoutOk().then(function(dataremote) {
-        t.equal(dataremote, 'Ok')
-        t.end()
-    })
-})
-
-test('asyncTimeoutError', function(t) {
-    remoteFunctions
-        .asyncTimeoutError()
-        .then(function(dataremote) {
-            t.equal(1, 2, 'this should not happen')
-        })
-        .catch(function(e) {
-            t.equal(e, 'Error')
-            t.end()
-        })
-})
-
-test('asyncPromiseOk', function(t) {
-    remoteFunctions.asyncPromiseOk().then(function(dataremote) {
-        t.equal(dataremote, 'Ok')
-        t.end()
-        server.listener.close()
-    })
-})
-
-if (transportName === 'local') {
-    try {
-        eval('async () => {}')
-        require('../ES6/call')
-    } catch (e) {}
-}
diff --git a/test/.protocol/call2.js b/test/.protocol/call2.js
deleted file mode 100644
index e4bbea4..0000000
--- a/test/.protocol/call2.js
+++ /dev/null
@@ -1,201 +0,0 @@
-var test = require('tape')
-var dopServer = require('../.proxy').create()
-var dopClient = require('../.proxy').create()
-var dopClientClient = require('../.proxy').create()
-dopServer.env = 'SERVER'
-dopClient.env = 'CLIENT'
-dopClientClient.env = 'CLIENTCLIENT'
-dopServer.data.object_inc = 7
-
-var transportName = process.argv[2] || 'local'
-var transportListen = require('dop-transports').listen[transportName]
-var transportConnect = require('dop-transports').connect[transportName]
-
-var server = dopServer.listen({ transport: transportListen })
-var client = dopClient.connect({
-    transport: transportConnect,
-    listener: server
-})
-var clientlisten = dopClient.listen({ transport: transportListen, port: 5555 })
-var clientclient = dopClientClient.connect({
-    transport: transportConnect,
-    url: 'ws://localhost:5555/dop',
-    listener: clientlisten
-})
-
-var objServer = dopServer.register({
-    string: function() {
-        return 'Hello world'
-    },
-    undefined: function() {
-        // returning nothing
-    },
-    object: function(f) {
-        return { hola: 'mundo' }
-    },
-    sum: function(a, b) {
-        tglobal.equal(
-            objServer,
-            this,
-            'Scope when calling remote is the same that calling locally'
-        )
-        return a + b
-    },
-    resolve: function(req) {
-        tglobal.equal(req.hasOwnProperty('node'), true, 'hasOwnProperty node')
-        tglobal.equal(
-            req.hasOwnProperty('resolve'),
-            true,
-            'hasOwnProperty resolve'
-        )
-        tglobal.equal(
-            req.hasOwnProperty('reject'),
-            true,
-            'hasOwnProperty reject'
-        )
-        req.resolve('resolved')
-    },
-    resolveAsync: function(req) {
-        setTimeout(function() {
-            req.resolve('resolveAsync')
-        }, 100)
-        return req
-    },
-    reject: function(req) {
-        req.reject('rejected')
-    },
-    reject0: function(req) {
-        req.reject(0)
-    },
-    function: function(f) {
-        tglobal.equal(f, null, 'when passing a function this must be null')
-        return function() {}
-    }
-})
-
-dopServer.onSubscribe(function() {
-    return objServer
-})
-
-var objClient = dopClient.register({})
-dopClient.onSubscribe(function() {
-    return objClient
-})
-
-test('TESTING RESOLVE AN REJECTS', function(t) {
-    client
-        .subscribe()
-        .into(objClient)
-        .then(function(obj) {
-            tglobal = t
-            obj.string()
-                .then(function(value) {
-                    t.equal('Hello world', value, 'Returning a string')
-                    return obj.undefined()
-                })
-                .then(function(value) {
-                    t.equal(undefined, value, 'Returning nothing')
-                    return obj.object(function() {})
-                })
-                .then(function(value) {
-                    t.equal(value.hola, 'mundo', 'Returning an object')
-                    return obj.sum(2, 2)
-                })
-                .then(function(value) {
-                    t.equal(value, 4, 'Passing two parameters')
-                    return obj.resolve()
-                })
-                .then(function(value) {
-                    t.equal(value, 'resolved', 'req.resolve instead of return')
-                    return obj.resolveAsync()
-                })
-                .then(function(value) {
-                    t.equal(value, 'resolveAsync', 'Resolved async')
-                    return objServer.resolveAsync(dopClient.core.createAsync())
-                })
-                .then(function(value) {
-                    t.equal(value, 'resolveAsync', 'Resolved async local')
-                    return obj.reject()
-                })
-                .catch(function(value) {
-                    t.equal(value, 'rejected', 'req.reject')
-                    // t.equal(value, null, 'req.function')
-                    return obj.reject0()
-                })
-                .catch(function(value) {
-                    t.equal(value, 0, 'Rejecting cero value')
-                    delete objServer.sum
-                    return obj.sum()
-                })
-                .catch(function(value) {
-                    t.equal(
-                        value,
-                        dopClient.core.error.reject_remote[3],
-                        dopClient.core.error.reject_remote.FUNCTION_NOT_FOUND
-                    )
-                    return obj.function(function() {})
-                })
-                .then(function(value) {
-                    t.equal(value, null, 'obj.function')
-                    t.end()
-                })
-        })
-})
-
-test('CALLING A FUNCTIONS BY TWO LEVELS', function(t) {
-    objClient.sum = function(a, b) {
-        return a + a + b + b
-    }
-
-    clientclient.subscribe().then(function(obj) {
-        tglobal = t
-        obj.string()
-            .then(function(value) {
-                t.equal('Hello world', value, 'Returning a string')
-                return obj.undefined()
-            })
-            .then(function(value) {
-                t.equal(undefined, value, 'Returning nothing')
-                return obj.object(function() {})
-            })
-            .then(function(value) {
-                t.equal(value.hola, 'mundo', 'Returning an object')
-                return obj.sum(1, 1)
-            })
-            .then(function(value) {
-                t.equal(value, 4, 'Passing two parameters')
-                return obj.resolve()
-            })
-            .then(function(value) {
-                t.equal(value, 'resolved', 'req.resolve instead of return')
-                return obj.resolveAsync()
-            })
-            .then(function(value) {
-                t.equal(value, 'resolveAsync', 'Resolved async')
-                return obj.reject()
-            })
-            .catch(function(value) {
-                t.equal(value, 'rejected', 'req.reject')
-                return obj.reject0()
-            })
-            .catch(function(value) {
-                t.equal(value, 0, 'Rejecting cero value')
-                delete objClient.sum
-                return obj.sum()
-            })
-            .catch(function(value) {
-                t.equal(
-                    value,
-                    dopClient.core.error.reject_remote[3],
-                    dopClient.core.error.reject_remote.FUNCTION_NOT_FOUND
-                )
-                return obj.function(function() {})
-            })
-            .then(function(value) {
-                t.equal(value, null, 'obj.function')
-                t.end()
-                server.listener.close()
-                clientlisten.listener.close()
-            })
-    })
-})
diff --git a/test/.protocol/getrequest.js b/test/.protocol/getrequest.js
deleted file mode 100644
index 1957f77..0000000
--- a/test/.protocol/getrequest.js
+++ /dev/null
@@ -1,116 +0,0 @@
-var test = require('tape')
-var dopServer = require('../.proxy').create()
-var dopClient = require('../.proxy').create()
-var transportName = process.argv[2] || 'local'
-var transportListen = require('dop-transports').listen[transportName]
-var transportConnect = require('dop-transports').connect[transportName]
-dopServer.env = 'SERVER'
-dopClient.env = 'CLIENT'
-
-// this code would be in node.js
-var server = dopServer.listen({ transport: transportListen })
-// this code would be in browser
-var client = dopClient.connect({
-    transport: transportConnect,
-    listener: server
-})
-
-var tGlobal // used to test outside tests clausures
-
-var localFunctions = dopServer.register({
-    checkRequestObject: function() {
-        const request = dopServer.getRequest(arguments)
-        tGlobal.equal(request instanceof Promise, true)
-        tGlobal.equal(request.hasOwnProperty('resolve'), true)
-        tGlobal.equal(request.hasOwnProperty('reject'), true)
-        // tGlobal.equal(request.node instanceof dopServer.core.node, true)
-    },
-    checkRequestObjectAsArgs: function() {
-        const args = Array.prototype.slice.call(arguments, 0)
-        const request = dopServer.getRequest(args)
-        tGlobal.equal(request instanceof Promise, true)
-        tGlobal.equal(request.hasOwnProperty('resolve'), true)
-        tGlobal.equal(request.hasOwnProperty('reject'), true)
-        // tGlobal.equal(request.node instanceof dopServer.core.node, true)
-    },
-    checkNodePropertyRemote: function() {
-        const request = dopServer.getRequest(arguments)
-        tGlobal.equal(request instanceof Promise, true)
-        tGlobal.equal(request.hasOwnProperty('resolve'), true)
-        tGlobal.equal(request.hasOwnProperty('reject'), true)
-        tGlobal.equal(request.node instanceof dopServer.core.node, true)
-    },
-    checkNodePropertyLocal: function() {
-        const request = dopServer.getRequest(arguments)
-        tGlobal.equal(request instanceof Promise, true)
-        tGlobal.equal(request.hasOwnProperty('resolve'), true)
-        tGlobal.equal(request.hasOwnProperty('reject'), true)
-        tGlobal.equal(request.hasOwnProperty('node'), false)
-    },
-    resolvingSquare: function(number) {
-        const request = dopServer.getRequest(arguments)
-        request.resolve(number * number)
-        return request
-    }
-})
-
-// server
-dopServer.onSubscribe(function() {
-    return localFunctions
-})
-
-var remoteFunctions
-test('Subscribing', function(t) {
-    client.subscribe().then(function(obj) {
-        remoteFunctions = obj
-        t.deepEqual(Object.keys(remoteFunctions), Object.keys(localFunctions))
-        t.end()
-    })
-})
-
-test('checkRequestObject', function(t) {
-    tGlobal = t
-    remoteFunctions.checkRequestObject()
-    localFunctions.checkRequestObject()
-    // t.equal()
-    t.end()
-})
-
-test('checkRequestObjectAsArgs', function(t) {
-    tGlobal = t
-    remoteFunctions.checkRequestObject()
-    localFunctions.checkRequestObject()
-    // t.equal()
-    t.end()
-})
-
-test('checkNodePropertyRemote', function(t) {
-    tGlobal = t
-    remoteFunctions.checkNodePropertyRemote()
-    t.end()
-})
-
-test('checkNodePropertyLocal', function(t) {
-    tGlobal = t
-    localFunctions.checkNodePropertyLocal()
-    t.end()
-})
-
-test('resolving', function(t) {
-    var number = 5
-    localFunctions
-        .resolvingSquare(number)
-        .then(square => {
-            t.equal(square, number * number)
-            return remoteFunctions.resolvingSquare(number)
-        })
-        .then(square => {
-            t.equal(square, number * number)
-            t.end()
-        })
-})
-
-test('closing', function(t) {
-    t.end()
-    server.listener.close()
-})
diff --git a/test/.protocol/patchs.js b/test/.protocol/patchs.js
deleted file mode 100644
index d33a930..0000000
--- a/test/.protocol/patchs.js
+++ /dev/null
@@ -1,75 +0,0 @@
-var test = require('tape')
-var dop = require('../.proxy').create()
-var dopClient1 = require('../.proxy').create()
-var dopClient2 = require('../.proxy').create()
-var transportName = process.argv[2] || 'local'
-var transportListen = require('dop-transports').listen[transportName]
-var transportConnect = require('dop-transports').connect[transportName]
-
-dop.env = 'SERVER'
-dop.data.object_inc = 25
-dopClient1.env = 'CLIENT1'
-dopClient2.env = 'CLIENT2'
-
-var server = dop.listen({ transport: transportListen })
-var client1 = dopClient1.connect({
-    transport: transportConnect,
-    listener: server
-})
-var client2 = dopClient2.connect({
-    transport: transportConnect,
-    listener: server
-})
-
-var objServer = dop.register({
-    number: 1,
-    subobject: {},
-    array: []
-})
-var objClient1
-var objClient2
-dop.onSubscribe(function() {
-    return objServer
-})
-
-test('TWO CLIENTS SUBCRIBED AND BOTH RECEIVE MULTIPLE MUTATIONS', function(t) {
-    client1.subscribe().then(function(obj) {
-        client2.subscribe().then(function(obj2) {
-            objClient1 = obj
-            objClient2 = obj2
-            t.deepEqual(
-                objServer,
-                objClient1,
-                'objClient1 deepEqual objServer before mutations'
-            )
-            t.deepEqual(
-                objServer,
-                objClient2,
-                'objClient2 deepEqual objServer before mutations'
-            )
-
-            dop.set(objServer, 'number', 'first')
-            dop.set(objServer, 'array', [2, 2, 2])
-            dop.set(objServer, 'tres', 3)
-            dop.set(objServer, 'cuatro', 4444)
-            dop.set(objServer, 'cinco', 'elcinco')
-
-            setTimeout(function() {
-                t.deepEqual(
-                    objServer,
-                    objClient1,
-                    'objClient1 deepEqual objServer after mutations'
-                )
-                t.deepEqual(
-                    objServer,
-                    objClient2,
-                    'objClient2 deepEqual objServer after mutations'
-                )
-                server.listener.close()
-                t.end()
-            }, 1000)
-        })
-    })
-})
-
-// // More test todo...
diff --git a/test/.protocol/subscribe.js b/test/.protocol/subscribe.js
deleted file mode 100644
index 727ef9d..0000000
--- a/test/.protocol/subscribe.js
+++ /dev/null
@@ -1,783 +0,0 @@
-var test = require('tape')
-var dopServer = require('../.proxy').create()
-var dopClient = require('../.proxy').create()
-var dopClient2 = require('../.proxy').create()
-var dopClientClient = require('../.proxy').create()
-dopServer.env = 'SERVER'
-dopClient.env = 'CLIENT'
-dopClient2.env = 'CLIENT2'
-dopClientClient.env = 'CLIENTCLIENT'
-
-var dop = dopServer
-var isTarget = function(object) {
-    return dop.isRegistered(object) && dop.getObjectTarget(object) === object
-}
-var isProxy = function(object) {
-    return dop.isRegistered(object) && dop.getObjectProxy(object) === object
-}
-
-test('SUBSCRIBE TESTS', function(tt) {
-    var test = function(name, cb) {
-        tt.test(name, {}, cb)
-    }
-
-    var transportName = process.argv[2] || 'local'
-    var transportListen = require('dop-transports').listen[transportName]
-    var transportConnect = require('dop-transports').connect[transportName]
-    var serverClient
-    var server = dopServer.listen({ transport: transportListen })
-    var client = dopClient.connect({
-        transport: transportConnect,
-        listener: server
-    })
-    var client2 = dopClient2.connect({
-        transport: transportConnect,
-        listener: server
-    })
-    server.on('connect', function(node) {
-        if (typeof serverClient == 'undefined') serverClient = node
-    })
-    var clientlistening = dopClient.listen({
-        transport: transportListen,
-        port: 5555
-    })
-    var clientclient = dopClientClient.connect({
-        transport: transportConnect,
-        url: 'ws://localhost:5555/dop',
-        listener: clientlistening
-    })
-    clientlistening.on('connect', function(node) {
-        if (typeof clientClient == 'undefined') clientClient = node
-    })
-
-    test('Before onSubscribe is defined', function(t) {
-        client.subscribe('DATA').catch(function(err) {
-            t.equal(
-                err,
-                dopClient.core.error.reject_remote[1],
-                'Object not found'
-            )
-            t.end()
-        })
-    })
-
-    test('Client subscribe synchronously', function(t) {
-        var DATA = {
-            string: 'string',
-            boolean: true,
-            number: -123,
-            Infinity: -Infinity,
-            float: 1.234153454354341,
-            long: 12313214234312324353454534534,
-            undefined: undefined,
-            null: null,
-            regexp: /molamazo/g,
-            date: new Date(),
-            '~DOP': [],
-            und: '~U',
-            inf: '~I',
-            fun: '~F',
-            nan: '~N',
-            reg: '~R',
-            deep: {
-                deeper: { the: 'end' }
-            }
-        }
-        var serverClient
-        dopServer.onSubscribe(function(name, req) {
-            if (name === 'DATA') {
-                serverClient = req.node
-                t.equal(isTarget(DATA), false, 'Is not registered yet')
-                return DATA
-            } else if (name === 'DATA_DEEP') {
-                t.equal(dopServer.isRegistered(DATA), true, 'Is registered')
-                t.equal(isTarget(DATA), true, 'Is target')
-                t.equal(isProxy(DATA.deep), true, 'Is proxy')
-                return DATA.deep
-            } else if (name === 'DATA_DEEPER') {
-                return dopServer.getObjectTarget(DATA.deep.deeper)
-            } else if (name === 'ASYNC') {
-                setTimeout(function() {
-                    req.resolve(dopServer.getObjectProxy(DATA))
-                }, 10)
-                return req
-            } else if (name === 'INNER-COPY') {
-                return { 0: Math.random(), 1: DATA }
-            } else if (name === 'REJECT') {
-                req.reject({ message: 'Subscription rejected' })
-            } else if (name === 'RESOLVESTRING') {
-                req.resolve('RESOLVESTRING')
-                setTimeout(t.end.bind(t), 10)
-            }
-        })
-
-        var objClient, objectDataServer, objectDataClient
-        client
-            .subscribe('DATA')
-            .then(function(obj) {
-                objClient = obj
-                objectDataServer = dopServer.data.object[1]
-                objectDataClient = dopClient.data.object[1]
-                t.deepEqual(obj, DATA, 'deepEqual data received from server')
-                t.deepEqual(
-                    objectDataServer.object,
-                    dopServer.getObjectProxy(DATA),
-                    'Same object stored in server'
-                )
-                t.equal(
-                    objectDataServer.node[serverClient.token].subscriber,
-                    1,
-                    'Client subscriber'
-                )
-                t.equal(
-                    objectDataServer.node[serverClient.token].owner,
-                    0,
-                    'Client is not owner'
-                )
-                t.equal(
-                    objectDataClient.node[client.token].subscriber,
-                    0,
-                    'Server is not subscriber'
-                )
-                t.equal(
-                    objectDataClient.node[client.token].owner,
-                    1,
-                    'Server is owner'
-                )
-                return client.subscribe('DATA_DEEP')
-            })
-            .then(function(obj_deep) {
-                t.equal(
-                    obj_deep,
-                    objClient.deep,
-                    'DATA_DEEP is the same object'
-                )
-                t.deepEqual(
-                    obj_deep,
-                    DATA.deep,
-                    'DATA_DEEP is deepEqual to original data'
-                )
-                return client.subscribe('DATA_DEEPER')
-            })
-            .then(function(obj_deeper) {
-                t.equal(
-                    obj_deeper,
-                    objClient.deep.deeper,
-                    'DATA_DEEPER is the same object'
-                )
-                t.deepEqual(
-                    obj_deeper,
-                    DATA.deep.deeper,
-                    'DATA_DEEPER is deepEqual to original data'
-                )
-                return client.subscribe('ASYNC')
-            })
-            .then(function(obj) {
-                t.equal(obj, objClient, 'DATA is the same object')
-                t.deepEqual(obj, DATA, 'DATA is deepEqual to original data')
-                return client.subscribe('INNER-COPY')
-            })
-            .then(function(obj) {
-                objectDataServer2 = dopServer.data.object[2]
-                objectDataClient2 = dopClient.data.object[2]
-                t.notEqual(
-                    objectDataServer.object,
-                    objectDataServer2.object[1],
-                    'Not equal to DATA'
-                )
-                t.deepEqual(
-                    objectDataServer.object,
-                    objectDataServer2.object[1],
-                    'deepEqual to DATA'
-                )
-                t.deepEqual(dopClient.getObjectId(obj), 2, 'Correct objectId')
-                t.deepEqual(obj[1], DATA, 'deepEqual data received from server')
-                t.deepEqual(
-                    objectDataServer2.node[serverClient.token].subscriber,
-                    1,
-                    'Client subscriber'
-                )
-                t.deepEqual(
-                    objectDataServer2.node[serverClient.token].owner,
-                    0,
-                    'Client is not owner'
-                )
-                t.deepEqual(
-                    objectDataClient2.node[client.token].subscriber,
-                    0,
-                    'Server is not subscriber'
-                )
-                t.deepEqual(
-                    objectDataClient2.node[client.token].owner,
-                    2,
-                    'Server is owner'
-                )
-                return client.subscribe('REJECT')
-            })
-            .catch(function(err) {
-                t.equal(
-                    err.message,
-                    'Subscription rejected',
-                    'Subscription rejected'
-                )
-                return client.subscribe()
-            })
-            .catch(function(err) {
-                t.equal(
-                    err,
-                    dopClient.core.error.reject_remote[1],
-                    'Object not found subscription rejected'
-                )
-                return client.subscribe('RESOLVESTRING')
-            })
-        // .catch(function(err){
-        //     console.log( 123131, typeof err );
-        //     t.end();
-        // })
-    })
-
-    test('Client subscribe asynchronously same object', function(t) {
-        var objectServer = { test: Math.random() }
-        dopServer.onSubscribe(function() {
-            return objectServer
-        })
-
-        var objectClient
-        client.subscribe().then(function(obj) {
-            objectClient = obj
-        })
-        client.subscribe().then(function(obj) {
-            t.equal(obj, objectClient, 'Same object')
-            t.deepEqual(objectServer, objectClient, 'deepEqual to objectServer')
-            t.end()
-        })
-    })
-
-    test('Client subscribe asynchronously different objects', function(t) {
-        dopServer.onSubscribe(function() {
-            return { test: Math.random() }
-        })
-
-        var objectClient1, objectClient2
-        client.subscribe().then(function(obj) {
-            objectClient1 = obj
-        })
-        client.subscribe().then(function(obj) {
-            objectClient2 = obj
-        })
-        client.subscribe().then(function(obj) {
-            var objectClient3 = obj
-            var objectId1 = dopClient.getObjectId(objectClient1)
-            var objectId2 = dopClient.getObjectId(objectClient2)
-            var objectId3 = dopClient.getObjectId(objectClient3)
-            t.equal(objectId1, objectId2 - 1, 'Correct ID order')
-            t.equal(objectId2, objectId3 - 1, 'Correct ID order2')
-            t.equal(
-                objectClient1.hasOwnProperty('test'),
-                true,
-                'objectClient1 hasOwnProperty test'
-            )
-            t.equal(
-                objectClient2.hasOwnProperty('test'),
-                true,
-                'objectClient2 hasOwnProperty test'
-            )
-            t.equal(
-                objectClient3.hasOwnProperty('test'),
-                true,
-                'objectClient3 hasOwnProperty test'
-            )
-            t.notDeepEqual(objectClient1, objectClient2)
-            t.notDeepEqual(objectClient2, objectClient3)
-            t.notDeepEqual(objectClient1, objectClient3)
-            t.end()
-        })
-    })
-
-    test('Server subscribe client object', function(t) {
-        var objClient = { test: Math.random() }
-        dopClient.onSubscribe(function() {
-            return objClient
-        })
-        serverClient.subscribe().then(function(objServer) {
-            var objectDataServer =
-                dopServer.data.object[dopServer.getObjectId(objServer)]
-            var objectDataClient =
-                dopClient.data.object[dopServer.getObjectId(objClient)]
-            t.equal(
-                objServer.hasOwnProperty('test'),
-                true,
-                'obj hasOwnProperty test'
-            )
-            t.deepEqual(objServer, objClient)
-            t.equal(
-                objectDataClient.node[client.token].subscriber,
-                1,
-                'Server is subscriber'
-            )
-            t.equal(
-                objectDataClient.node[client.token].owner,
-                0,
-                'Server is not owner'
-            )
-            t.equal(
-                objectDataServer.node[serverClient.token].subscriber,
-                0,
-                'Client is not subscriber'
-            )
-            t.equal(
-                objectDataServer.node[serverClient.token].owner > 0,
-                true,
-                'Client is owner'
-            )
-            t.end()
-        })
-    })
-
-    test('Server subscribe client object that is from server', function(t) {
-        var objServer = { test: Math.random() },
-            objClient
-        dopServer.onSubscribe(function() {
-            return objServer
-        })
-        dopClient.onSubscribe(function() {
-            return objClient
-        })
-        client.subscribe().then(function(obj) {
-            objClient = obj
-            serverClient.subscribe().then(function(objServer2) {
-                t.notEqual(objServer2, dopServer.getObjectProxy(objServer))
-                t.equal(
-                    dopServer.getObjectId(objServer2),
-                    dopServer.getObjectId(objServer) + 1,
-                    'Different ids'
-                )
-                t.equal(
-                    objServer2.hasOwnProperty('test'),
-                    true,
-                    'obj hasOwnProperty test'
-                )
-                t.deepEqual(objServer, objClient)
-                t.deepEqual(objServer, objServer2)
-                t.deepEqual(objClient, obj)
-
-                var objectDataServer =
-                    dopServer.data.object[dopServer.getObjectId(objServer)]
-                var objectDataServer2 =
-                    dopServer.data.object[dopServer.getObjectId(objServer2)]
-                var objectDataClient =
-                    dopClient.data.object[dopServer.getObjectId(objClient)]
-                t.equal(
-                    objectDataServer.node[serverClient.token].subscriber,
-                    1,
-                    'Client is subscriber'
-                )
-                t.equal(
-                    objectDataServer.node[serverClient.token].owner,
-                    0,
-                    'Client is not owner'
-                )
-                t.equal(
-                    objectDataServer2.node[serverClient.token].subscriber,
-                    0,
-                    'Client is not subscriber'
-                )
-                t.equal(
-                    objectDataServer2.node[serverClient.token].owner > 0,
-                    true,
-                    'Client is owner'
-                )
-                t.equal(
-                    objectDataClient.node[client.token].subscriber,
-                    1,
-                    'Server is subscriber'
-                )
-                t.equal(
-                    objectDataClient.node[client.token].owner > 0,
-                    true,
-                    'Server is not owner'
-                )
-
-                t.end()
-            })
-        })
-    })
-
-    test('Client subscribe into object that is not register yet', function(t) {
-        var objServer = { test: Math.random() },
-            objClient = { test: Math.random() },
-            objClientSnap = dopClient.util.merge({}, objClient)
-
-        t.deepEqual(objClient, objClientSnap, 'Are equivalent before subcribe')
-        t.equal(dopClient.isRegistered(objClient), false, 'Is not registered')
-
-        dopServer.onSubscribe(function() {
-            return objServer
-        })
-        client
-            .subscribe()
-            .into(objClient)
-            .then(function(obj) {
-                t.equal(
-                    dopClient.isRegistered(objClient),
-                    true,
-                    'Is registered'
-                )
-                t.notDeepEqual(
-                    objClient,
-                    objClientSnap,
-                    'Are not equivalent before subcribe'
-                )
-                t.deepEqual(
-                    objClient,
-                    objServer,
-                    'Are equivalent with server objetc'
-                )
-                t.equal(
-                    obj,
-                    dopClient.getObjectProxy(objClient),
-                    'objClient and obj are the same object'
-                )
-                t.end()
-            })
-    })
-
-    test('Client subscribe into object that is already registered', function(t) {
-        var objServer = { test: Math.random() },
-            objClient = dopClient.register({ test: Math.random() }),
-            objClientSnap = dopClient.util.merge({}, objClient)
-
-        t.deepEqual(objClient, objClientSnap, 'Are equivalent before subcribe')
-        t.equal(dopClient.isRegistered(objClient), true, 'Is registered')
-
-        dopServer.onSubscribe(function() {
-            return objServer
-        })
-        client
-            .subscribe()
-            .into(objClient)
-            .then(function(obj) {
-                t.equal(dopClient.isRegistered(obj), true, 'Is registered')
-                t.notDeepEqual(
-                    objClient,
-                    objClientSnap,
-                    'Are not equivalent before subcribe'
-                )
-                t.deepEqual(
-                    objClient,
-                    objServer,
-                    'Are equivalent with server objetc'
-                )
-                t.equal(obj, objClient, 'objClient and obj are the same object')
-                t.end()
-            })
-    })
-
-    test('Server <-> Client subscribe into the same object', function(t) {
-        var objServer = { test: Math.random() },
-            objClient = { test: Math.random() }
-
-        dopServer.onSubscribe(function() {
-            return objServer
-        })
-        dopClient.onSubscribe(function() {
-            return objClient
-        })
-
-        client
-            .subscribe()
-            .into(objClient)
-            .then(function(obj) {
-                t.equal(
-                    obj,
-                    dopClient.getObjectProxy(objClient),
-                    'Same object client'
-                )
-                serverClient
-                    .subscribe()
-                    .into(objServer)
-                    .then(function(obj2) {
-                        t.equal(
-                            obj2,
-                            dopServer.getObjectProxy(objServer),
-                            'Same object server'
-                        )
-                        t.deepEqual(objClient, objServer)
-                        var objectDataServer =
-                            dopServer.data.object[
-                                dopServer.getObjectId(objServer)
-                            ]
-                        var objectDataClient =
-                            dopClient.data.object[
-                                dopServer.getObjectId(objClient)
-                            ]
-                        t.equal(
-                            objectDataServer.node[serverClient.token]
-                                .subscriber,
-                            1,
-                            'Client is subscriber'
-                        )
-                        t.equal(
-                            objectDataServer.node[serverClient.token].owner > 0,
-                            true,
-                            'Client is owner'
-                        )
-                        t.equal(
-                            objectDataClient.node[client.token].subscriber,
-                            1,
-                            'Server is subscriber'
-                        )
-                        t.equal(
-                            objectDataClient.node[client.token].owner > 0,
-                            true,
-                            'Server is owner'
-                        )
-                        t.end()
-                    })
-            })
-    })
-
-    test('Multiple Clients subscribe Same object', function(t) {
-        var objectServer = { test: Math.random() }
-        dopServer.onSubscribe(function() {
-            return objectServer
-        })
-
-        var objectClient
-        client.subscribe().then(function(obj) {
-            objectClient = obj
-            t.deepEqual(objectClient, objectServer, 'deepEqual to objectServer')
-        })
-        client2.subscribe().then(function(obj) {
-            t.deepEqual(obj, objectClient, 'deepEqual to objectClient1')
-            t.deepEqual(obj, objectServer, 'deepEqual to objectServer')
-            t.end()
-        })
-    })
-
-    test('Multiple Clients subscribe different objects', function(t) {
-        dopServer.onSubscribe(function() {
-            return { test: Math.random() }
-        })
-        var objectClient
-        client.subscribe().then(function(obj) {
-            objectClient = obj
-        })
-        client2.subscribe().then(function(obj) {
-            t.notDeepEqual(obj, objectClient, 'notDeepEqual to objectClient1')
-            t.end()
-        })
-    })
-
-    test('Server -> Client -> ClientClient', function(t) {
-        var objServer = { test: Math.random() },
-            objClient
-        dopServer.onSubscribe(function() {
-            return objServer
-        })
-        dopClient.onSubscribe(function() {
-            return objClient
-        })
-        client.subscribe().then(function(obj) {
-            objClient = obj
-            clientclient.subscribe().then(function(obj2) {
-                t.deepEqual(obj2, objServer, 'deepEqual to objServer')
-
-                var objectDataServer =
-                    dopServer.data.object[dopServer.getObjectId(objServer)]
-                var objectDataClient =
-                    dopClient.data.object[dopClient.getObjectId(objClient)]
-                var objectDataClientClient =
-                    dopClientClient.data.object[dopClient.getObjectId(obj2)]
-                t.equal(
-                    objectDataServer.node[serverClient.token].subscriber,
-                    1,
-                    'Client is subscriber'
-                )
-                t.equal(
-                    objectDataServer.node[serverClient.token].owner,
-                    0,
-                    'Client is not owner'
-                )
-                t.equal(
-                    objectDataClient.node[client.token].subscriber,
-                    0,
-                    'Server is not subscriber'
-                )
-                t.equal(
-                    objectDataClient.node[client.token].owner > 0,
-                    true,
-                    'Server is owner'
-                )
-
-                t.equal(
-                    objectDataClient.node[clientClient.token].subscriber,
-                    1,
-                    'ClientClient is subscriber'
-                )
-                t.equal(
-                    objectDataClient.node[clientClient.token].owner,
-                    0,
-                    'ClientClient is not owner'
-                )
-                t.equal(
-                    objectDataClientClient.node[clientclient.token].subscriber,
-                    0,
-                    'Client is not subscriber'
-                )
-                t.equal(
-                    objectDataClientClient.node[clientclient.token].owner > 0,
-                    true,
-                    'Client is owner'
-                )
-
-                t.end()
-            })
-        })
-    })
-
-    test('Server <-> Client <-> ClientClient into the same object', function(t) {
-        var objServer = { test: Math.random() },
-            objClient = { test: Math.random() },
-            objClientClient = { test: Math.random() }
-
-        dopServer.onSubscribe(function() {
-            return objServer
-        })
-        dopClient.onSubscribe(function() {
-            return objClient
-        })
-        dopClientClient.onSubscribe(function() {
-            return objClientClient
-        })
-
-        clientclient
-            .subscribe()
-            .into(objClientClient)
-            .then(function(obj) {
-                t.equal(
-                    obj,
-                    dopClientClient.getObjectProxy(objClientClient),
-                    'Same object clientclient'
-                )
-                client
-                    .subscribe()
-                    .into(objClient)
-                    .then(function(obj) {
-                        t.equal(
-                            obj,
-                            dopClient.getObjectProxy(objClient),
-                            'Same object client'
-                        )
-                        serverClient
-                            .subscribe()
-                            .into(objServer)
-                            .then(function(obj) {
-                                t.equal(
-                                    obj,
-                                    dopServer.getObjectProxy(objServer),
-                                    'Same object server'
-                                )
-                                clientClient
-                                    .subscribe()
-                                    .into(objClient)
-                                    .then(function(obj) {
-                                        t.equal(
-                                            obj,
-                                            dopClient.getObjectProxy(objClient),
-                                            'Same object client'
-                                        )
-                                        t.deepEqual(
-                                            objServer,
-                                            objClient,
-                                            'deepEqual to objServer objClient'
-                                        )
-                                        t.deepEqual(
-                                            objClient,
-                                            objClientClient,
-                                            'deepEqual to objServer objClientClient'
-                                        )
-
-                                        var objectDataServer =
-                                            dopServer.data.object[
-                                                dopServer.getObjectId(objServer)
-                                            ]
-                                        var objectDataClient =
-                                            dopClient.data.object[
-                                                dopServer.getObjectId(objClient)
-                                            ]
-                                        var objectDataClientClient =
-                                            dopClientClient.data.object[
-                                                dopServer.getObjectId(
-                                                    objClientClient
-                                                )
-                                            ]
-                                        t.equal(
-                                            objectDataServer.node[
-                                                serverClient.token
-                                            ].subscriber,
-                                            1,
-                                            'Client is subscriber'
-                                        )
-                                        t.equal(
-                                            objectDataServer.node[
-                                                serverClient.token
-                                            ].owner > 0,
-                                            true,
-                                            'Client is owner'
-                                        )
-                                        t.equal(
-                                            objectDataClient.node[client.token]
-                                                .subscriber,
-                                            1,
-                                            'Server is subscriber'
-                                        )
-                                        t.equal(
-                                            objectDataClient.node[client.token]
-                                                .owner > 0,
-                                            true,
-                                            'Server is owner'
-                                        )
-                                        t.equal(
-                                            objectDataClient.node[
-                                                clientClient.token
-                                            ].subscriber,
-                                            1,
-                                            'ClientClient is subscriber'
-                                        )
-                                        t.equal(
-                                            objectDataClient.node[
-                                                clientClient.token
-                                            ].owner > 0,
-                                            true,
-                                            'ClientClient is owner'
-                                        )
-                                        t.equal(
-                                            objectDataClientClient.node[
-                                                clientclient.token
-                                            ].subscriber,
-                                            1,
-                                            'Client is subscriber'
-                                        )
-                                        t.equal(
-                                            objectDataClientClient.node[
-                                                clientclient.token
-                                            ].owner > 0,
-                                            true,
-                                            'Client is owner'
-                                        )
-                                        t.end()
-                                        server.listener.close()
-                                        clientlistening.listener.close()
-                                    })
-                            })
-                    })
-            })
-    })
-
-    tt.end()
-
-    // test("Server -> Client -> ClientClient -> Server", function(t) {
-    // to do...
-    // })
-})
diff --git a/test/.protocol/unsubscribe.js b/test/.protocol/unsubscribe.js
deleted file mode 100644
index 5c76e44..0000000
--- a/test/.protocol/unsubscribe.js
+++ /dev/null
@@ -1,132 +0,0 @@
-var test = require('tape')
-var dopServer = require('../.proxy').create()
-var dopClient = require('../.proxy').create()
-dopServer.env = 'SERVER'
-dopClient.env = 'CLIENT'
-dopClient.data.object_inc = 7
-
-var transportName = process.argv[2] || 'local'
-var transportListen = require('dop-transports').listen[transportName]
-var transportConnect = require('dop-transports').connect[transportName]
-var serverClient
-var server = dopServer.listen({ transport: transportListen })
-var client = dopClient.connect({
-    transport: transportConnect,
-    listener: server
-})
-server.on('connect', function(node) {
-    if (typeof serverClient == 'undefined') serverClient = node
-})
-
-test('Client unsubscribe Server', function(t) {
-    var objectServer = dopServer.register({ test: Math.random() })
-
-    t.equal(
-        dopServer.data.object[1],
-        undefined,
-        'Object data not stored on server yet'
-    )
-
-    dopServer.onSubscribe(function() {
-        return objectServer
-    })
-
-    var objectClient = dopClient.register({})
-
-    t.equal(
-        dopClient.data.object[1],
-        undefined,
-        'Object data not stored on client yet'
-    )
-
-    client
-        .subscribe()
-        .into(objectClient)
-        .then(function(obj) {
-            var objectDataServer = dopServer.data.object[1]
-            var objectDataClient = dopClient.data.object[7]
-
-            t.deepEqual(
-                objectDataServer.node[serverClient.token].subscriber,
-                1,
-                'Client subscriber'
-            )
-            t.deepEqual(
-                objectDataServer.node[serverClient.token].owner,
-                0,
-                'Client is not owner'
-            )
-            t.deepEqual(
-                objectDataClient.node[client.token].subscriber,
-                0,
-                'Server is not subscriber'
-            )
-            t.deepEqual(
-                objectDataClient.node[client.token].owner,
-                1,
-                'Server is owner'
-            )
-
-            client.unsubscribe(objectClient).then(function() {
-                t.deepEqual(
-                    dopServer.data.object[1],
-                    undefined,
-                    'Object in server removed from data.object'
-                )
-                t.deepEqual(
-                    dopServer.data.object[7],
-                    undefined,
-                    'Object in client removed from data.object'
-                )
-                t.deepEqual(
-                    objectDataServer.node[serverClient.token].subscriber,
-                    0,
-                    'Client is not subscriber'
-                )
-                t.deepEqual(
-                    objectDataServer.node[serverClient.token].owner,
-                    0,
-                    'Client is not owner'
-                )
-                t.deepEqual(
-                    objectDataClient.node[client.token].subscriber,
-                    0,
-                    'Server is not subscriber'
-                )
-                t.deepEqual(
-                    objectDataClient.node[client.token].owner,
-                    0,
-                    'Server not is owner'
-                )
-                t.end()
-            })
-        })
-})
-
-// https://github.com/DistributedObjectProtocol/dop/issues/4
-test('Subscribe Unsubscribe and Subscribe same object', function(t) {
-    var x = dopServer.register({ hi: 'world' })
-    dopServer.onSubscribe(function(name) {
-        return x
-    })
-
-    var a, b
-    client
-        .subscribe()
-        .then(function(_) {
-            a = _
-            return client.unsubscribe(a)
-        })
-        .then(function() {
-            return client.subscribe()
-        })
-        .then(function(_) {
-            b = _
-            t.deepEqual(x, a, 'x and a deepEqual')
-            t.deepEqual(a, b, 'a and b deepEqual')
-            t.notEqual(x, a, 'x and a notEqual')
-            t.notEqual(a, b, 'a and b notEqual')
-            server.listener.close()
-            t.end()
-        })
-})
diff --git a/test/.proxy.js b/test/.proxy.js
deleted file mode 100644
index 3bacced..0000000
--- a/test/.proxy.js
+++ /dev/null
@@ -1,29 +0,0 @@
-var dop = require('../dist/dop.nodejs')
-
-// // This is not necesary now but we can use to manipulate some methods depending of test arguments
-// if (process.argv[2] === 'proxy') {
-//     dop.set = function(o, p, v) {
-//         var old = o[o]
-//         try {
-//             o[p] = v
-//         } catch (e) {}
-//         return v
-//     }
-//     dop.get = function(o, p) {
-//         return o[p]
-//     }
-//     dop.del = function(o, p) {
-//         var a = delete o[p]
-//         return a
-//     }
-//     var create = dop.create
-//     dop.create = function() {
-//         var d = create()
-//         d.set = dop.set
-//         d.get = dop.get
-//         d.del = dop.del
-//         return d
-//     }
-// }
-
-module.exports = dop
diff --git a/test/ES6/call.js b/test/ES6/call.js
deleted file mode 100644
index 85689e3..0000000
--- a/test/ES6/call.js
+++ /dev/null
@@ -1,332 +0,0 @@
-const test = require('tape')
-const { connect } = require('../protocol/.connect.js')
-
-const localFunctions = {
-    okSync: function(req) {
-        return 'Ok'
-    },
-    okAsync: async function(req) {
-        return 'Ok'
-    },
-    okPromise: function(req) {
-        return new Promise(function(resolve, reject) {
-            resolve('Ok')
-        })
-    },
-
-    undefinedSync: function(req) {},
-    undefinedAsync: async function(req) {},
-    undefinedPromise: function(req) {
-        return new Promise(function(resolve, reject) {
-            resolve(undefined)
-        })
-    },
-
-    errorSync: function(req) {
-        throw 'Error'
-    },
-    errorAsync: async function(req) {
-        throw 'Error'
-    },
-    errorPromise: function(req) {
-        return new Promise(function(resolve, reject) {
-            reject('Error')
-        })
-    },
-
-    timeoutSync: function(req) {
-        setTimeout(function() {
-            req.resolve('Ok')
-        }, 10)
-        return req
-    },
-    timeoutAsync: async function(req) {
-        setTimeout(function() {
-            req.resolve('Ok')
-        }, 10)
-        return req
-    },
-    timeoutPromise: async function(req) {
-        return new Promise(function(resolve, reject) {
-            setTimeout(function() {
-                req.resolve('Ok')
-            }, 10)
-            resolve(req)
-        })
-    },
-
-    reqresolveSync: function(req) {
-        return req.resolve('ok')
-    },
-    reqresolveAsync: async function(req) {
-        return req.resolve('ok')
-    },
-    reqresolvePromise: function(req) {
-        return new Promise(function(resolve, reject) {
-            resolve(req.resolve('ok'))
-        })
-    },
-
-    presolveSync: function(req) {
-        return Promise.resolve('ok')
-    },
-    presolveAsync: async function(req) {
-        return Promise.resolve('ok')
-    },
-    presolvePromise: function(req) {
-        return new Promise(function(resolve, reject) {
-            resolve(Promise.resolve('ok'))
-        })
-    },
-
-    reqrejectSync: function(req) {
-        return req.reject('Error')
-    },
-    reqrejectAsync: async function(req) {
-        return req.reject('Error')
-    },
-    reqrejectPromise: function(req) {
-        return new Promise(function(resolve, reject) {
-            reject(req.reject('Error'))
-        })
-    },
-
-    prejectSync: function(req) {
-        return Promise.reject('Error')
-    },
-    prejectAsync: async function(req) {
-        return Promise.reject('Error')
-    },
-    prejectPromise: function(req) {
-        return new Promise(function(resolve, reject) {
-            resolve(Promise.reject('Error'))
-        })
-    }
-}
-
-let remoteFunctions
-let closeServer
-test('Matching same values', async function(t) {
-    const { dopServer, nodeClient, close } = await connect(
-        t,
-        false
-    )
-    closeServer = close
-    dopServer.onSubscribe(() => localFunctions)
-    remoteFunctions = await nodeClient.subscribe()
-    t.deepEqual(Object.keys(remoteFunctions), Object.keys(localFunctions))
-    t.end()
-})
-
-test('Ok', async function(t) {
-    t.equal(await remoteFunctions.okSync(), await remoteFunctions.okAsync())
-    t.equal(await remoteFunctions.okSync(), await remoteFunctions.okPromise())
-    t.equal(await remoteFunctions.okSync(), await localFunctions.okSync())
-    t.equal(await remoteFunctions.okAsync(), await localFunctions.okAsync())
-    t.equal(await remoteFunctions.okPromise(), await localFunctions.okPromise())
-    t.end()
-})
-
-test('Undefined', async function(t) {
-    t.equal(
-        await remoteFunctions.undefinedSync(),
-        await remoteFunctions.undefinedAsync()
-    )
-    t.equal(
-        await remoteFunctions.undefinedSync(),
-        await remoteFunctions.undefinedPromise()
-    )
-    t.equal(
-        await remoteFunctions.undefinedSync(),
-        await localFunctions.undefinedSync()
-    )
-    t.equal(
-        await remoteFunctions.undefinedAsync(),
-        await localFunctions.undefinedAsync()
-    )
-    t.equal(
-        await remoteFunctions.undefinedPromise(),
-        await localFunctions.undefinedPromise()
-    )
-    t.end()
-})
-
-test('Error', async function(t) {
-    try {
-        await remoteFunctions.errorSync()
-        t.equal(1, 2, 'this should not happen')
-    } catch (e) {
-        t.equal(e, 'Error')
-    }
-    try {
-        await remoteFunctions.errorAsync()
-        t.equal(1, 2, 'this should not happen')
-    } catch (e) {
-        t.equal(e, 'Error')
-    }
-    try {
-        await remoteFunctions.errorPromise()
-        t.equal(1, 2, 'this should not happen')
-    } catch (e) {
-        t.equal(e, 'Error')
-    }
-
-    try {
-        await localFunctions.errorSync()
-        t.equal(1, 2, 'this should not happen')
-    } catch (e) {
-        t.equal(e, 'Error')
-    }
-    try {
-        await localFunctions.errorAsync()
-        t.equal(1, 2, 'this should not happen')
-    } catch (e) {
-        t.equal(e, 'Error')
-    }
-    try {
-        await localFunctions.errorPromise()
-        t.equal(1, 2, 'this should not happen')
-    } catch (e) {
-        t.equal(e, 'Error')
-    }
-
-    t.end()
-})
-
-test('timeout', async function(t) {
-    t.equal(
-        await remoteFunctions.timeoutSync(),
-        await remoteFunctions.timeoutAsync()
-    )
-    t.equal(
-        await remoteFunctions.timeoutSync(),
-        await remoteFunctions.timeoutPromise()
-    )
-    t.end()
-})
-
-test('req.resolve', async function(t) {
-    t.equal(
-        await remoteFunctions.reqresolveSync(),
-        await remoteFunctions.reqresolveAsync()
-    )
-    t.equal(
-        await remoteFunctions.reqresolveSync(),
-        await remoteFunctions.reqresolvePromise()
-    )
-    t.end()
-})
-
-test('Promise.resolve', async function(t) {
-    t.equal(
-        await remoteFunctions.reqresolveSync(),
-        await remoteFunctions.reqresolveAsync()
-    )
-    t.equal(
-        await remoteFunctions.reqresolveSync(),
-        await remoteFunctions.reqresolvePromise()
-    )
-    t.end()
-})
-
-test('req.reject', async function(t) {
-    try {
-        await remoteFunctions.reqrejectSync()
-        t.equal(1, 2, 'this should not happen')
-    } catch (e) {
-        t.equal(e, 'Error')
-    }
-    try {
-        await remoteFunctions.reqrejectAsync()
-        t.equal(1, 2, 'this should not happen')
-    } catch (e) {
-        t.equal(e, 'Error')
-    }
-    try {
-        await remoteFunctions.reqrejectPromise()
-        t.equal(1, 2, 'this should not happen')
-    } catch (e) {
-        t.equal(e, 'Error')
-    }
-
-    t.end()
-})
-
-test('Promise.reject', async function(t) {
-    try {
-        await remoteFunctions.prejectSync()
-        t.equal(1, 2, 'this should not happen')
-    } catch (e) {
-        t.equal(e, 'Error')
-    }
-    try {
-        await remoteFunctions.prejectAsync()
-        t.equal(1, 2, 'this should not happen')
-    } catch (e) {
-        t.equal(e, 'Error')
-    }
-    try {
-        await remoteFunctions.prejectPromise()
-        t.equal(1, 2, 'this should not happen')
-    } catch (e) {
-        t.equal(e, 'Error')
-    }
-
-    t.end()
-    closeServer()
-})
-
-test('basic three nodes', async t => {
-    const {
-        dopServer,
-        dopClient,
-        nodeClient,
-        nodeSubClient,
-        close
-    } = await connect(t)
-    const objectServer = {
-        times: (a, b) => a * b
-    }
-    dopServer.onSubscribe(() => objectServer)
-    const objectClient = await nodeClient.subscribe()
-    dopClient.onSubscribe(() => objectClient)
-    const objectSubClient = await nodeSubClient.subscribe()
-
-    t.equal(await objectClient.times(5, 5), 25)
-    t.equal(await objectSubClient.times(4, 2), 8)
-    close()
-})
-
-test('throwing error three nodes', async t => {
-    const {
-        dopServer,
-        dopClient,
-        nodeClient,
-        nodeSubClient,
-        close
-    } = await connect(t)
-    const objectServer = {
-        error: customerror => {
-            throw customerror
-        }
-    }
-    dopServer.onSubscribe(() => objectServer)
-    const objectClient = await nodeClient.subscribe()
-    dopClient.onSubscribe(() => objectClient)
-    const objectSubClient = await nodeSubClient.subscribe()
-
-    try {
-        await objectClient.error('Custom remote error')
-        t.equal(true, false, 'This should not happen one')
-    } catch (e) {
-        t.equal(e, 'Custom remote error')
-    }
-
-    try {
-        await objectSubClient.error('Custom sub remote error')
-        t.equal(true, false, 'This should not happen two')
-    } catch (e) {
-        t.equal(e, 'Custom sub remote error')
-    }
-    close()
-})
diff --git a/test/ES6/subscribe.js b/test/ES6/subscribe.js
deleted file mode 100644
index c3a947b..0000000
--- a/test/ES6/subscribe.js
+++ /dev/null
@@ -1,161 +0,0 @@
-const test = require('tape')
-const dop = require('../.proxy')
-const { connect } = require('../protocol/.connect.js')
-
-test('basic', async t => {
-    const { dopServer, nodeClient, close } = await connect(t)
-    const objectServer = dopServer.register({ hello: 'world' })
-    dopServer.onSubscribe(() => objectServer)
-    const objectClient = await nodeClient.subscribe()
-    t.deepEqual(objectServer, objectClient)
-    close()
-})
-
-test('basic no registered', async t => {
-    const { dopServer, nodeClient, close } = await connect(t)
-    const objectServer = { hello: 'world' }
-    dopServer.onSubscribe(() => objectServer)
-    const objectClient = await nodeClient.subscribe()
-    t.deepEqual(objectServer, objectClient)
-    close()
-})
-
-test('basic deep', async t => {
-    const { dopServer, nodeClient, close } = await connect(t)
-    const objectServer = dopServer.register({
-        hello: 'world',
-        deep: { value: 'Hello World' }
-    })
-    dopServer.onSubscribe(() => objectServer.deep)
-    const objectClient = await nodeClient.subscribe()
-    t.deepEqual(objectServer.deep, objectClient)
-    close()
-})
-
-test('subscribe same object twice', async t => {
-    const { dopServer, nodeClient, close } = await connect(t)
-    const objectServer = dopServer.register({ hello: 'world' })
-    dopServer.onSubscribe(() => objectServer)
-    const objectClient = await nodeClient.subscribe()
-    const objectClient2 = await nodeClient.subscribe()
-    t.deepEqual(objectServer, objectClient)
-    t.equal(objectClient, objectClient2)
-    close()
-})
-
-test('subscribe same subobject twice', async t => {
-    const { dopServer, nodeClient, close } = await connect(t)
-    const objectServer = dopServer.register({
-        hello: 'world',
-        deep: { hola: 'mundo' }
-    })
-    dopServer.onSubscribe(() => objectServer.deep)
-    const objectClient = await nodeClient.subscribe()
-    const objectClient2 = await nodeClient.subscribe()
-    t.deepEqual(objectServer.deep, objectClient)
-    t.equal(objectClient, objectClient2)
-    close()
-})
-
-test('subscribe same object twice and then subscribe sub object', async t => {
-    const { dopServer, nodeClient, close } = await connect(t)
-    const objectServer = dopServer.register({
-        hello: 'world',
-        deep: { hola: 'mundo' }
-    })
-    dopServer.onSubscribe(() => objectServer)
-    const objectClient = await nodeClient.subscribe()
-    const objectClient2 = await nodeClient.subscribe()
-    t.deepEqual(objectServer, objectClient)
-    t.equal(objectClient, objectClient2)
-    dopServer.onSubscribe(() => objectServer.deep)
-    const objectClient3 = await nodeClient.subscribe()
-    t.deepEqual(objectServer.deep, objectClient3)
-    t.deepEqual(objectClient.deep, objectClient3)
-    t.notEqual(objectClient.deep, objectClient3)
-    close()
-})
-
-test('into()', async t => {
-    const { dopServer, nodeClient, close } = await connect(t)
-    const objectServer = { hello: 'world' }
-    const objectClient = {}
-    t.notDeepEqual(objectServer, objectClient)
-    dopServer.onSubscribe(() => objectServer)
-    const objectClientInto = await nodeClient.subscribe().into(objectClient)
-    t.deepEqual(objectServer, objectClient)
-    t.notEqual(objectClientInto, objectClient)
-    t.equal(objectClientInto, dop.getObjectProxy(objectClient))
-    close()
-})
-
-test('into() preregistered', async t => {
-    const { dopServer, dopClient, nodeClient, close } = await connect(t)
-    const objectServer = { hello: 'world' }
-    const objectClient = dopClient.register({})
-    t.notDeepEqual(objectServer, objectClient)
-    dopServer.onSubscribe(() => objectServer)
-    const objectClientInto = await nodeClient.subscribe().into(objectClient)
-    t.deepEqual(objectServer, objectClient)
-    t.equal(objectClientInto, objectClient)
-    close()
-})
-
-test('into() client deep object', async t => {
-    const { dopServer, nodeClient, close } = await connect(t)
-    const objectServer = { hello: 'world' }
-    const objectClient = { deep: {} }
-    t.notDeepEqual(objectServer, objectClient.deep)
-    dopServer.onSubscribe(() => objectServer)
-    await nodeClient.subscribe().into(objectClient.deep)
-    t.deepEqual(objectServer, objectClient.deep)
-    close()
-})
-
-test('into() client and server deep object', async t => {
-    const { dopServer, nodeClient, close } = await connect(t)
-    const objectServer = { hello: 'world', deep: { value: 'helloworld' } }
-    const objectClient = { hola: 'mundo', deep: { value: 'holamundo' } }
-    t.notDeepEqual(objectServer, objectClient)
-    dopServer.onSubscribe(() => objectServer.deep)
-    await nodeClient.subscribe().into(objectClient.deep)
-    t.deepEqual(objectServer.deep, objectClient.deep)
-    t.notDeepEqual(objectServer, objectClient)
-    close()
-})
-
-test('observe mutations after subscription', async t => {
-    const { dopServer, dopClient, nodeClient, close } = await connect(t)
-    const objectServer = { hello: 'world', second: 'arg' }
-    const objectClient = dopClient.register({})
-    dopServer.onSubscribe(() => objectServer)
-    dopClient
-        .createObserver(mutations => {
-            t.equal(mutations.length, 2)
-            t.deepEqual(objectServer, objectClient)
-            close()
-        })
-        .observeAll(objectClient)
-    await nodeClient.subscribe().into(objectClient)
-})
-
-test('basic three nodes', async t => {
-    const {
-        dopServer,
-        dopClient,
-        nodeClient,
-        nodeSubClient,
-        close
-    } = await connect(t)
-    const objectServer = {
-        hello: 'world',
-        deep: { hola: 'mundo' }
-    }
-    dopServer.onSubscribe(() => objectServer)
-    const objectClient = await nodeClient.subscribe()
-    dopClient.onSubscribe(() => objectClient)
-    const objectSubClient = await nodeSubClient.subscribe()
-    t.deepEqual(objectServer, objectClient)
-    t.deepEqual(objectSubClient, objectClient)
-    close()
-})
diff --git a/test/action.js b/test/action.js
deleted file mode 100644
index 466ed9d..0000000
--- a/test/action.js
+++ /dev/null
@@ -1,59 +0,0 @@
-var test = require('tape')
-// require('tabe').createStream( test );
-var dop = require('./.proxy').create()
-
-test('basic collect', function(t) {
-    var object = dop.register({
-        a: true,
-        b: true
-    })
-    var dopAction = dop.action(function(value) {
-        dop.set(object, 'a', value)
-        dop.set(object, 'b', value)
-        return value
-    })
-    var observer = dop.createObserver(function(mutations) {
-        t.equal(mutations.length, 2)
-    })
-    observer.observeObject(object)
-    dopAction(false)
-    t.end()
-})
-
-test('two actions', function(t) {
-    var object = dop.register({
-        a: true,
-        b: true
-    })
-    var dopAction1 = dop.action(function(value) {
-        dop.set(object, 'a', value)
-        dop.set(object, 'b', value)
-        return value
-    })
-    var dopAction2 = dop.action(function(value) {
-        dop.set(object, 'a', value)
-        dop.set(object, 'b', value)
-        return value
-    })
-    var timesObserved = 0
-    var observer = dop.createObserver(function(mutations) {
-        timesObserved += 1
-        t.equal(mutations.length, 2)
-    })
-    observer.observeObject(object)
-    dopAction1(111)
-    dopAction2(222)
-    t.equal(timesObserved, 2)
-    t.end()
-})
-
-test('same output', function(t) {
-    var myAction = function(n) {
-        return n
-    }
-    var dopAction = dop.action(myAction)
-    var arg = 'argument1'
-    t.equal(myAction(arg), arg)
-    t.equal(myAction(arg), dopAction(arg))
-    t.end()
-})
diff --git a/test/api.js b/test/api.js
new file mode 100644
index 0000000..e4afb3d
--- /dev/null
+++ b/test/api.js
@@ -0,0 +1,39 @@
+import test from 'ava'
+import dop from '../src'
+import { createNode } from '../src'
+
+const exported = {
+    version: true,
+    factory: true,
+    merge: true,
+    encode: true,
+    decode: true,
+    producePatch: true,
+    applyPatch: true,
+    createNode: true,
+    createStore: true,
+    TYPE: true,
+    addType: true,
+}
+
+test('Checking exported params', function (t) {
+    const keys = {}
+    Object.keys(dop).forEach((key) => {
+        keys[key] = true
+    })
+    t.deepEqual(keys, exported)
+})
+
+test('Checking factory', function (t) {
+    const dopcopy = dop.factory()
+    t.deepEqual(Object.keys(dop), Object.keys(dopcopy))
+    t.is(dop.factory, dopcopy.factory)
+    t.not(dop.createNode, dopcopy.createNode)
+})
+
+test('Types must be same instance when creating new dop', function (t) {
+    const dopcopy = dop.factory()
+    t.is(dop.TYPE.Inner, dopcopy.TYPE.Inner)
+    t.is(dop.TYPE.Replace, dopcopy.TYPE.Replace)
+    t.is(dop.TYPE.Splice, dopcopy.TYPE.Splice)
+})
diff --git a/test/apply_patch.js b/test/apply_patch.js
new file mode 100644
index 0000000..1764191
--- /dev/null
+++ b/test/apply_patch.js
@@ -0,0 +1,1053 @@
+import test from 'ava'
+import { applyPatch, TYPE } from '../src'
+import { testPatchUnpatch } from './utils'
+
+test('1 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = { a: 'b' }
+    const patch = { a: 'c' }
+    const expected = { a: 'c' }
+    const fnpatch = (d) => {
+        d.a = 'c'
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('2 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = { a: 'b' }
+    const patch = { b: 'c' }
+    const expected = { a: 'b', b: 'c' }
+    const fnpatch = (d) => {
+        d.b = 'c'
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('3 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = { a: 'b' }
+    const patch = { a: TYPE.Delete() }
+    const expected = {}
+    const fnpatch = (d) => {
+        delete d.a
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('4 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = { a: 'b', b: 'c' }
+    const patch = { a: TYPE.Delete() }
+    const expected = { b: 'c' }
+    const fnpatch = (d) => {
+        delete d.a
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('5 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = { a: ['b'] }
+    const patch = { a: 'c' }
+    const expected = { a: 'c' }
+    const fnpatch = (d) => {
+        d.a = 'c'
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('6 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = { a: 'c' }
+    const patch = { a: ['b'] }
+    const expected = { a: ['b'] }
+    const fnpatch = (d) => {
+        d.a = ['b']
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('7 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = { a: { b: 'c' } }
+    const patch = { a: { b: 'd', c: TYPE.Delete() } }
+    const expected = { a: { b: 'd' } }
+    const fnpatch = (d) => {
+        d.a.b = 'd'
+        delete d.a.c
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('8 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = { a: {} }
+    const patch = { a: [1] }
+    const expected = { a: [1] }
+    const fnpatch = (d) => {
+        d.a = [1]
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('9 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = ['a', 'b']
+    const patch = ['c', 'd']
+    const expected = ['c', 'd']
+
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('10 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = { a: 'b' }
+    const patch = ['c']
+    const expected = ['c']
+
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('11 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = { a: 'foo' }
+    const patch = null
+    const expected = null
+
+    testPatchUnpatch({ t, target, patch, expected, serialize: false })
+})
+
+test('12 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = { a: 'foo' }
+    const patch = 'bar'
+    const expected = 'bar'
+
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('13 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = { e: null }
+    const patch = { a: 1 }
+    const expected = { e: null, a: 1 }
+    const fnpatch = (d) => {
+        d.a = 1
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('13/b', function (t) {
+    const target = { e: TYPE.Delete() }
+    const patch = { a: 1 }
+    const expected = { e: TYPE.Delete(), a: 1 }
+    const fnpatch = (d) => {
+        d.a = 1
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('13/c (not sure about this case)', function (t) {
+    const target = { e: TYPE.Delete() }
+    const patch = { a: 1, e: TYPE.Delete() }
+    const expected = { a: 1 }
+    const fnpatch = (d) => {
+        d.a = 1
+        delete d.e
+    }
+
+    // not sure about this case because the unpatch would result in {}
+    testPatchUnpatch({ t, target, patch, expected, reverse: false, fnpatch })
+})
+
+test('14 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = 'string' // [1, 2]
+    const patch = { a: 'b', c: TYPE.Delete() }
+    const expected = { a: 'b' }
+
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('15 / https://tools.ietf.org/html/rfc7386 ', function (t) {
+    const target = {}
+    const patch = { a: { bb: { ccc: TYPE.Delete() } } }
+    const expected = { a: { bb: {} } }
+    const fnpatch = (d) => {
+        d.a = { bb: { ccc: true } }
+        delete d.a.bb.ccc
+    }
+
+    testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        fnpatch,
+        // reversefn: false,
+    })
+})
+
+test('api: patch result is the same that patch argument', function (t) {
+    const target = { hello: 1, world: 2, name: 'enzo' }
+    const patch_expected = { hello: 3, new: 4, world: TYPE.Delete() }
+    const { patch } = applyPatch(target, patch_expected)
+
+    t.is(patch, patch_expected)
+    t.deepEqual(patch, patch_expected)
+    t.deepEqual(target, { hello: 3, new: 4, name: 'enzo' })
+})
+
+test('api: applyPatch as function instead of patch', function (t) {
+    const target = { hello: 1, world: 2, name: 'enzo' }
+
+    const { patch } = applyPatch(target, (d) => {
+        d.hello = 3
+        d.new = 4
+        delete d.world
+    })
+
+    t.deepEqual(patch, { hello: 3, new: 4, world: TYPE.Delete() })
+    t.deepEqual(target, { hello: 3, new: 4, name: 'enzo' })
+})
+
+test('basic mutation', function (t) {
+    const target = { number: 1 }
+    const patch = { number: 2 }
+    const expected = { number: 2 }
+    const fnpatch = (d) => {
+        d.number = 2
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('basic mutations', function (t) {
+    const func1 = () => {}
+    const func2 = () => {}
+    const target = { number: 1, bool: false, string: 'hello', func: func1 }
+    const patch = { number: 2, bool: true, string: 'world', func: func2 }
+    const expected = { number: 2, bool: true, string: 'world', func: func2 }
+    const fnpatch = (d) => {
+        d.number = 2
+        d.bool = true
+        d.string = 'world'
+        d.func = func2
+    }
+
+    testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        fnpatch,
+        encodedecode: false,
+    })
+})
+
+test('value didnt exists', function (t) {
+    const target = {}
+    const patch = { value: true }
+    const expected = { value: true }
+    const fnpatch = (d) => {
+        d.value = true
+    }
+
+    testPatchUnpatch({ t, target, patch, fnpatch, expected })
+})
+
+test('deletion', function (t) {
+    const target = { value: 12345 }
+    const patch = { value: TYPE.Delete() }
+    const expected = {}
+    const fnpatch = (d) => {
+        delete d.value
+    }
+
+    testPatchUnpatch({ t, target, patch, fnpatch, expected })
+})
+
+test('patch new target should not be same target', function (t) {
+    const target = { prop: 123 }
+    const patch = { prop: { deep: true } }
+    const expected = { prop: { deep: true } }
+
+    applyPatch(target, patch)
+    t.not(target.prop, patch.prop)
+    t.deepEqual(target, expected)
+})
+
+test('multiple mutations levels', function (t) {
+    const target = { value: false, prop: false, last: { value: false } }
+    const patch = { value: true, prop: { deep: true }, last: { value: true } }
+    const expected = {
+        value: true,
+        prop: { deep: true },
+        last: { value: true },
+    }
+    const fnpatch = (d) => {
+        d.value = true
+        d.prop = { deep: true }
+        d.last.value = true
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('from deep to other', function (t) {
+    const target = { value: { more: false } }
+    const patch = { value: true }
+    const expected = { value: true }
+    const fnpatch = (d) => {
+        d.value = true
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('deep value', function (t) {
+    const target = { value: { more: false } }
+    const patch = { value: { more: true } }
+    const expected = { value: { more: true } }
+    const fnpatch = (d) => {
+        d.value.more = true
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('deep value should not create root target, just mutate the props', function (t) {
+    const target = { value: { a: 1 } }
+    const patch = { value: { a: 2, b: 3 } }
+    const expected = { value: { a: 2, b: 3 } }
+
+    const { unpatch, mutations } = testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+    })
+    t.is(mutations.length, 2)
+    t.deepEqual(unpatch, { value: { a: 1, b: unpatch.value.b } })
+})
+
+test('mutating multiple levels', function (t) {
+    const target = { value: { more: false }, other: false }
+    const patch = { value: { more: true }, other: true }
+    const expected = { value: { more: true }, other: true }
+    const fnpatch = (d) => {
+        d.value.more = true
+        d.other = true
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('mutating multiple levels not defineds', function (t) {
+    const target = {}
+    const patch = { value: { more: true }, other: true }
+    const expected = { value: { more: true }, other: true }
+    const fnpatch = (d) => {
+        d.value = { more: true }
+        d.other = true
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('mutating multiple levels not defineds / inverted', function (t) {
+    const target = { value: { more: true }, other: true }
+    const patch = { value: TYPE.Delete(), other: TYPE.Delete() }
+    const expected = {}
+    const fnpatch = (d) => {
+        delete d.value
+        delete d.other
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('function to target', function (t) {
+    const target = { prop: () => {} }
+    const patch = { prop: {} }
+    const expected = { prop: {} }
+    const fnpatch = (d) => {
+        d.prop = {}
+    }
+
+    testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        encodedecode: false,
+        fnpatch,
+    })
+})
+
+test('target to function', function (t) {
+    const f = () => {}
+    const target = { prop: {} }
+    const patch = { prop: f }
+    const expected = { prop: f }
+    const fnpatch = (d) => {
+        d.prop = f
+    }
+
+    testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        encodedecode: false,
+        fnpatch,
+    })
+})
+
+test('plain to noplain', function (t) {
+    function Foo() {}
+    const instance = new Foo()
+    instance.prop = 2
+
+    const target = { prop: instance }
+    const patch = { prop: { deep: true } }
+    const expected = { prop: { deep: true } }
+    const fnpatch = (d) => {
+        d.prop = { deep: true }
+    }
+
+    const { mutations } = testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        encodedecode: false,
+        fnpatch,
+    })
+    t.is(mutations.length, 1)
+})
+
+test('noplain to plain', function (t) {
+    function Foo() {}
+    const instance = new Foo()
+    instance.prop = 2
+
+    const target = { prop: { deep: true } }
+    const patch = { prop: instance }
+    const expected = { prop: instance }
+    const fnpatch = (d) => {
+        d.prop = instance
+    }
+
+    testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        encodedecode: false,
+        fnpatch,
+    })
+})
+
+test.skip('syntax mutations', function (t) {
+    const target = {
+        value: false,
+        prop: false,
+        last: { value: false },
+        lastlast: { value: false },
+    }
+    const patch = {
+        value: true,
+        prop: { deep: true },
+        last: { value: true },
+        lastlast: true,
+    }
+
+    const { mutations } = applyPatch(target, patch)
+    t.is(mutations.length, 4)
+    t.deepEqual(mutations[0], {
+        prop: 'value',
+        old_value: false,
+        path: ['value'],
+        object: target,
+    })
+    t.deepEqual(mutations[1], {
+        prop: 'prop',
+        old_value: false,
+        path: ['prop'],
+        object: target,
+    })
+    t.deepEqual(mutations[2], {
+        prop: 'value',
+        old_value: false,
+        path: ['last', 'value'],
+        object: target.last,
+    })
+    t.deepEqual(mutations[3], {
+        prop: 'lastlast',
+        old_value: { value: false },
+        path: ['lastlast'],
+        object: target,
+    })
+})
+
+test.skip('syntax mutations on top level', function (t) {
+    const target = {}
+    const patch = 'bar'
+
+    const { mutations } = applyPatch(target, patch)
+    t.is(mutations.length, 1)
+    t.deepEqual(mutations[0], {
+        prop: '',
+        old_value: target,
+        path: [],
+        object: { '': patch },
+    })
+})
+
+test.skip('syntax mutations when using delete', function (t) {
+    const target = {}
+    const patch = TYPE.Delete()
+
+    const { mutations } = applyPatch(target, patch)
+    t.is(mutations.length, 1)
+    t.deepEqual(mutations[0], {
+        prop: '',
+        old_value: target,
+        path: [],
+        object: {},
+    })
+})
+
+test('Deep objects/array must be merged instead of referenced', function (t) {
+    const target = {}
+    const patch = { object: { a: true }, array: [{ b: true }] }
+    const { result } = applyPatch(target, patch)
+
+    t.is(result, target)
+    t.deepEqual(result, patch)
+    t.not(result.object, patch.object)
+    t.not(result.array, patch.array)
+    t.not(result.array[0], patch.array[0])
+})
+
+test('Deep objects/array must be merged instead of referenced 2', function (t) {
+    const target = { array: [0, 1, 2] }
+    const patch = { array: [3, 4] }
+    const expected = { array: [3, 4] }
+    const fnpatch = (d) => {
+        d.array = [3, 4]
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, reverse: false, fnpatch })
+    t.deepEqual(target.array, patch.array)
+    t.not(target.array, patch.array)
+    // console.log(target, patch)
+})
+
+test('array complex', function (t) {
+    const target = { array: [0, 1, 2], string: 'yep', arrstr: [1, 2] }
+    const patch = { array: [3, 4], string: ['a', 'b'], arrstr: '12' }
+    const expected = { array: [3, 4], string: ['a', 'b'], arrstr: '12' }
+    const fnpatch = (d) => {
+        d.array = [3, 4]
+        d.string = ['a', 'b']
+        d.arrstr = '12'
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('from object to array', function (t) {
+    const target = { objarr: { value: true } }
+    const patch = { objarr: [0, 1] }
+    const expected = { objarr: [0, 1] }
+    const fnpatch = (d) => {
+        d.objarr = [0, 1]
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('from array to object', function (t) {
+    const target = { objarr: [0, 1] }
+    const patch = { objarr: {} }
+    const expected = { objarr: [0, 1] }
+
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('should assign `null` values', function (t) {
+    const target = { value: false }
+    const patch = { value: null }
+    const expected = { value: null }
+    const fnpatch = (d) => {
+        d.value = null
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('should assign `undefined` values', function (t) {
+    const target = { value: false }
+    const patch = { value: undefined, value2: undefined }
+    const expected = { value: undefined, value2: undefined }
+    const fnpatch = (d) => {
+        d.value = undefined
+        d.value2 = undefined
+    }
+
+    testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        encodedecode: false,
+        fnpatch,
+    })
+})
+
+test('should assign non array/buffer/typed-array/plain-target source values directly', function (t) {
+    function Foo() {}
+    const values = [
+        new Foo(),
+        new Boolean(),
+        new Date(),
+        Foo,
+        new Number(),
+        new String(),
+        new RegExp(),
+    ]
+
+    const target = {}
+    const patch = { values: values }
+    const expected = { values: values }
+    const fnpatch = (d) => {
+        d.values = values
+    }
+
+    testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        encodedecode: false,
+        fnpatch,
+    })
+})
+
+test('same patch as target generate no mutations', function (t) {
+    const target = { value: false }
+    const patch = {}
+    const expected = { value: false }
+
+    const { mutations, unpatch } = testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+    })
+    t.is(mutations.length, 0)
+    t.deepEqual(unpatch, {})
+})
+
+test('same array as patch generate no mutations even if we mutate target object', function (t) {
+    const target = { array: [1, 2, 3] }
+    const original = target.array
+    original.push(4)
+    const patch = { array: target.array }
+    const expected = { array: [1, 2, 3, 4] }
+    const { mutations, unpatch } = testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        encodedecode: false,
+    })
+    t.is(mutations.length, 0)
+    t.deepEqual(unpatch, {})
+    t.is(target.array, original)
+})
+
+test('slice array as patch generate mutation', function (t) {
+    const target = { array: [1, 2, 3] }
+    const original = target.array
+    const patch = { array: target.array.slice(0).concat(4) }
+    const expected = { array: [1, 2, 3, 4] }
+    const fnpatch = (d) => {
+        d.array = target.array.slice(0).concat(4)
+    }
+
+    const { mutations, unpatch } = testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        fnpatch,
+    })
+    t.is(mutations.length, 1)
+    t.not(target.array, original)
+})
+
+test('no mutations', function (t) {
+    const target = { value: false }
+    const patch = {}
+    const expected = { value: false }
+
+    const { mutations, unpatch } = testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+    })
+    t.is(mutations.length, 0)
+    t.deepEqual(unpatch, {})
+})
+
+test('checking different types and mutating multiple deep values', function (t) {
+    const target = {
+        string: 'string',
+        obj: { lolo: 213 },
+        arr: [1, 2, 3, { La: 243534 }],
+        array: [32131],
+        arrobj: { 0: 4, 1: 5 },
+        d: {
+            a: 3131,
+            array: [1, 2, 3, { abc: 444 }],
+            d: {
+                d1: 432534,
+                d2: {
+                    d21: 143243,
+                    d22: {
+                        d221: 4353453,
+                        d223: {
+                            hola: 'mundo',
+                            undefined: 'undefined',
+                        },
+                    },
+                },
+            },
+            arrobj: ['a', 'b', 'c'],
+            g: 'fff',
+            d2: {
+                d22: {
+                    d222: 312432,
+                    d223: {
+                        hola: 'lalala',
+                    },
+                },
+            },
+        },
+    }
+    const patch = {
+        boolean: true,
+        number: -123,
+        Infinity: -Infinity,
+        float: 1.234153454354341,
+        long: 12313214234312324353454534534,
+        null: null,
+        undefined: undefined,
+        NaN: NaN,
+        symbol: Symbol('sym'),
+        date: new Date(),
+        regexp: /molamazo/g,
+        function: function () {
+            console.log(arguments)
+        },
+        obj: { lolo: 555 },
+        arr: [1, 2, 3, { La: 123 }],
+        array: [567, 2121],
+        arrobj: { 0: 1, 1: 2 },
+        d: {
+            a: 1234,
+            b: 5678,
+            c: 'othermore',
+            array: [1, 2, 3, { abc: 123 }],
+            d: {
+                d1: 13,
+                d2: {
+                    d21: 123,
+                    d22: {
+                        d221: 12,
+                        d223: {
+                            hola: 'hola',
+                            undefined: undefined,
+                        },
+                    },
+                },
+            },
+            arrobj: ['a', 'b', 'c', 'd'],
+            g: 123,
+            d2: {
+                d22: {
+                    d222: 555,
+                    d223: {
+                        hola: 'mundo',
+                    },
+                },
+            },
+        },
+    }
+    const expected = { ...patch, string: 'string' }
+
+    const { mutations } = testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        encodedecode: false,
+    })
+    t.is(mutations.length, 30)
+})
+
+// Inner arrays
+// Inner arrays
+// Inner arrays
+// Inner arrays
+// Inner arrays
+// Inner arrays
+// Inner arrays
+// Inner arrays
+// Inner arrays
+
+test('adding item to array ', function (t) {
+    const target = { objarr: [0, 1] }
+    const patch = { objarr: { 3: { hello: 'world' } } }
+    const expected = { objarr: [0, 1, undefined, { hello: 'world' }] }
+    const fnpatch = (d) => {
+        d.objarr[3] = { hello: 'world' }
+    }
+
+    const { mutations } = testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        fnpatch,
+    })
+    t.is(mutations.length, 2)
+    t.is(mutations[1].prop, 'length')
+})
+
+test('adding multiple item to array ', function (t) {
+    const target = { objarr: [0, 1] }
+    const patch = { objarr: { 3: 3, 5: 5 } }
+    const expected = { objarr: [0, 1, undefined, 3, undefined, 5] }
+    const fnpatch = (d) => {
+        d.objarr[3] = 3
+        d.objarr[5] = 5
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('changing length of array', function (t) {
+    const target = { objarr: [0, 1] }
+    const patch = { objarr: { length: 4 } }
+    const expected = { objarr: [0, 1, undefined, undefined] }
+    const fnpatch = (d) => {
+        d.objarr.length = 4
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('editing length of array and also adding extra values', function (t) {
+    const target = { objarr: [0, 1] }
+    const patch = { objarr: { 3: 3, length: 5 } }
+    const expected = { objarr: [0, 1, undefined, 3, undefined] }
+    const fnpatch = (d) => {
+        d.objarr[3] = 3
+        d.objarr.length = 5
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('Inner plain array', function (t) {
+    const target = [{ b: false }]
+    const patch = { 0: { b: true } }
+    const expected = [{ b: true }]
+    const fnpatch = (d) => {
+        d[0].b = true
+    }
+
+    const { result } = testPatchUnpatch({ t, target, patch, expected, fnpatch })
+    t.is(target, result)
+})
+
+test('Editing top level', function (t) {
+    const target = { array: ['a', 'b', 'c'] }
+    const patch = { array: { 0: 'A', 3: 'D', 4: 'E' } }
+    const expected = { array: ['A', 'b', 'c', 'D', 'E'] }
+    const fnpatch = (d) => {
+        d.array[0] = 'A'
+        d.array[3] = 'D'
+        d.array[4] = 'E'
+    }
+
+    testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        fnpatch,
+    })
+})
+
+test('Editing subobjects', function (t) {
+    const target = { array: [{ a: false }] }
+    const patch = { array: { 0: { a: true } } }
+    const expected = { array: [{ a: true }] }
+    const fnpatch = (d) => {
+        d.array[0].a = true
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('Deleting subobject', function (t) {
+    const target = { array: [{ a: true }] }
+    const patch = { array: { 0: { a: TYPE.Delete() } } }
+    const expected = { array: [{}] }
+    const fnpatch = (d) => {
+        delete d.array[0].a
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('Updating sub-subarray', function (t) {
+    const target = { array: [{ subarray: ['a'] }] }
+    const array = target.array
+    const subarray = target.array[0].subarray
+    const patch = {
+        array: { 0: { subarray: { 0: 'b' } } },
+    }
+    const expected = { array: [{ subarray: ['b'] }] }
+    const fnpatch = (d) => {
+        d.array[0].subarray[0] = 'b'
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+    t.is(array, target.array)
+    t.is(subarray, target.array[0].subarray)
+})
+
+test('Replacing sub-subarray', function (t) {
+    const target = { array: [{ subarray: ['a'] }] }
+    const array = target.array
+    const subarray = target.array[0].subarray
+    const patch = {
+        array: { 0: { subarray: ['b', 'c'] } },
+    }
+    const expected = { array: [{ subarray: ['b', 'c'] }] }
+    const fnpatch = (d) => {
+        d.array[0].subarray = ['b', 'c']
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+    t.is(array, target.array)
+    t.not(subarray, target.array[0].subarray)
+})
+
+test('Pushing subobjects', function (t) {
+    const target = ['A']
+    const patch = { 2: { a: true } }
+    const expected = ['A', undefined, { a: true }]
+    const fnpatch = (d) => {
+        d[2] = { a: true }
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('Editing subobject and creating a new one', function (t) {
+    const target = [{ a: false }]
+    const patch = { 0: { a: true }, 1: { b: true } }
+    const expected = [{ a: true }, { b: true }]
+    const fnpatch = (d) => {
+        d[0].a = true
+        d[1] = { b: true }
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('Mutating array of array with a splice', function (t) {
+    const target = { array: [{ array2: [{ array3: [1, 2] }] }] }
+    const instancearray = target.array[0].array2[0].array3
+    const expected = { array: [{ array2: [{ array3: [1, 2, 3] }] }] }
+    const patch = {
+        array: {
+            0: { array2: { 0: { array3: TYPE.Splice(2, 0, 3) } } },
+        },
+    }
+    const fnpatch = (d) => {
+        d.array[0].array2[0].array3.splice(2, 0, 3)
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+    t.is(instancearray, target.array[0].array2[0].array3)
+})
+
+test('Changing lengh array', function (t) {
+    const target = ['A']
+    const patch = { length: 2 }
+    const expected = ['A', undefined]
+    const fnpatch = (d) => {
+        d.length = 2
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('adding inner arrays', function (t) {
+    const target = { objarr: [0, 1, [false], [[]]] }
+    const patch = { objarr: { 2: [2], 3: { 0: { 0: [3] } } } }
+    const expected = { objarr: [0, 1, [2], [[[3]]]] }
+    const fnpatch = (d) => {
+        d.objarr[2] = [2]
+        d.objarr[3][0][0] = [3]
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('adding inner arrays as top level', function (t) {
+    const target = [0, 1, [false], [[]]]
+    const patch = { length: 5, 2: [2], 3: { 2: 4, 0: { 0: [3] } } }
+    const expected = [0, 1, [2], [[[3]], undefined, 4], undefined]
+    const fnpatch = (d) => {
+        d.length = 5
+        d[2] = [2]
+        d[3][0][0] = [3]
+        d[3][2] = 4
+    }
+
+    testPatchUnpatch({ t, target, patch, expected, fnpatch })
+})
+
+test('complex patch to array', function (t) {
+    const target = { objarr: [0, 1, { value: false }] }
+    const patch = {
+        objarr: {
+            2: { value: true },
+            4: { value: true },
+            5: TYPE.Multi([2, 3], TYPE.Swap(0, 1)),
+        },
+    }
+    const expected = {
+        objarr: [0, 1, { value: true }, undefined, { value: true }, [3, 2]],
+    }
+    const fnpatch = (d) => {
+        d.objarr[2] = { value: true }
+        d.objarr[4] = { value: true }
+        d.objarr[5] = TYPE.Multi([2, 3], TYPE.Swap(0, 1))
+    }
+
+    testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        fnpatch,
+    })
+})
diff --git a/test/collectGetters.js b/test/collectGetters.js
deleted file mode 100644
index bf863fa..0000000
--- a/test/collectGetters.js
+++ /dev/null
@@ -1,22 +0,0 @@
-var test = require('tape')
-// require('tabe').createStream( test );
-var dop = require('./.proxy').create()
-
-var object = dop.register({ a: 1, b: 2, deep: { deep2: { c: 3 } } })
-
-test('collectGetters', function(t) {
-    var stopCollect = dop.collectGetters()
-    dop.get(object, 'a')
-    dop.get(object.deep.deep2, 'c')
-    var stopCollect2 = dop.collectGetters()
-    dop.get(object, 'b')
-    var getters2 = stopCollect2()
-    var getters = stopCollect()
-    t.equal(getters.length, 4)
-    t.equal(getters2.length, 1)
-    var item = getters[0]
-    t.equal(item.hasOwnProperty('object'), true, 'object')
-    t.equal(item.hasOwnProperty('property'), true, 'property')
-    // t.equal(item.hasOwnProperty('path'), true, 'path')
-    t.end()
-})
diff --git a/test/collectors.js b/test/collectors.js
deleted file mode 100644
index 0c72eba..0000000
--- a/test/collectors.js
+++ /dev/null
@@ -1,125 +0,0 @@
-var test = require('tape')
-// require('tabe').createStream( test );
-var dop = require('./.proxy').create()
-
-var object = dop.register({})
-
-test('Collect as default', function(t) {
-    var collector = dop.collect()
-    dop.set(object, 'array', [])
-    t.equal(collector.mutations.length, 1)
-    t.equal(collector.mutations[0].object.array, object.array)
-    collector.destroy()
-    t.end()
-})
-
-test('Multiple collectors order', function(t) {
-    var collector1 = dop.collect()
-    var collector2 = dop.collect()
-    object.array.push(Math.random())
-    t.equal(collector1.mutations.length, 1)
-    t.equal(collector2.mutations.length, 0)
-    collector1.destroy()
-    collector2.destroy()
-    t.end()
-})
-
-test('Using collectFirst', function(t) {
-    var collector1 = dop.collect()
-    var collector2 = dop.collect(function() {
-        return 0
-    })
-    object.array.push(Math.random())
-    var collector3 = dop.collect()
-    object.array.push(Math.random())
-    var collector4 = dop.collect(function() {
-        return 0
-    })
-    object.array.push(Math.random())
-    t.equal(collector1.mutations.length, 0)
-    t.equal(collector2.mutations.length, 2)
-    t.equal(collector3.mutations.length, 0)
-    t.equal(collector4.mutations.length, 1)
-    collector1.destroy()
-    collector2.destroy()
-    collector3.destroy()
-    collector4.destroy()
-    t.end()
-})
-
-test('Active and inactive collectores', function(t) {
-    var collector1 = dop.collect()
-    object.array.push(Math.random())
-    var collector2 = dop.collect()
-    object.array.push(Math.random())
-    collector1.active = false
-    object.array.push(Math.random())
-    collector1.active = true
-    object.array.push(Math.random())
-    t.equal(collector1.mutations.length, 3)
-    t.equal(collector2.mutations.length, 1)
-    collector1.destroy()
-    collector2.destroy()
-    t.end()
-})
-
-test('Destroying', function(t) {
-    var collector1 = dop.collect()
-    object.array.push(Math.random())
-    var collector2 = dop.collect()
-    object.array.push(Math.random())
-    collector1.destroy()
-    object.array.push(Math.random())
-    t.equal(collector1.mutations.length, 2)
-    t.equal(collector2.mutations.length, 1)
-    collector1.destroy()
-    collector2.destroy()
-    t.end()
-})
-
-test('Filtering', function(t) {
-    var totalArray = object.array.length
-    var collector1 = dop.collect()
-    collector1.filter = function(mutation) {
-        return mutation.object.length === totalArray + 1
-    }
-    object.array.push(Math.random())
-    var collector2 = dop.collect()
-    object.array.push(Math.random())
-    t.equal(collector1.mutations.length, 1)
-    t.equal(collector2.mutations.length, 1)
-    object.array.push(Math.random())
-    t.equal(collector1.mutations.length, 1)
-    t.equal(collector2.mutations.length, 2)
-    delete collector1.filter
-    object.array.push(Math.random())
-    t.equal(collector1.mutations.length, 2)
-    t.equal(collector2.mutations.length, 2)
-    collector1.destroy()
-    collector2.destroy()
-    t.end()
-})
-
-test('Emit', function(t) {
-    var collector = dop.collect()
-    object.array.push(Math.random())
-    t.equal(collector.mutations.length, 1)
-    collector.emitWithoutDestroy()
-    t.equal(collector.mutations.length, 0)
-    object.array.push(Math.random())
-    t.equal(collector.mutations.length, 1)
-    collector.emit()
-    object.array.push(Math.random())
-    t.equal(collector.mutations.length, 0)
-    t.end()
-})
-
-test('Testing shadow option on set', function(t) {
-    var collector = dop.collect()
-    dop.set(object, 'noshadow', [2, 3, 4])
-    dop.set(object, 'shadow', [2, 3, 4], { shadow: true })
-    t.equal(collector.mutations.length, 1)
-    t.deepEqual(object.noshadow, object.shadow)
-    collector.destroy()
-    t.end()
-})
diff --git a/test/collectors_and_actions.js b/test/collectors_and_actions.js
deleted file mode 100644
index feaeaa1..0000000
--- a/test/collectors_and_actions.js
+++ /dev/null
@@ -1,100 +0,0 @@
-var test = require('tape')
-// require('tabe').createStream( test );
-var dop = require('./.proxy').create()
-
-var object = dop.register({
-    collector1: true,
-    collector2: true,
-    action1: true,
-    action2: true
-})
-
-function collector1() {
-    var collector = dop.collect()
-    dop.set(object, 'collector1', !object.collector1)
-    collector.emit()
-}
-
-function collector2() {
-    var collector = dop.collect()
-    collector1()
-    dop.set(object, 'collector2', !object.collector2)
-    collector.emit()
-}
-
-var action1 = dop.action(function() {
-    dop.set(object, 'action1', !object.action1)
-})
-
-var action2 = dop.action(function() {
-    action1()
-    dop.set(object, 'action2', !object.action2)
-})
-
-var allInOne = dop.action(function() {
-    action1()
-    action2()
-    collector1()
-    collector2()
-})
-
-function allInOneCollector() {
-    var collector = dop.collect()
-    action1()
-    action2()
-    collector1()
-    collector2()
-    collector.emit()
-}
-
-test('Two collectors', function(t) {
-    var times = 0
-    var observer = dop.createObserver(function(m) {
-        t.equal(m.length, 2)
-        times += 1
-    })
-    observer.observeObject(object)
-    collector2()
-    t.equal(times, 1)
-    observer.destroy()
-    t.end()
-})
-
-test('Two actions', function(t) {
-    var times = 0
-    var observer = dop.createObserver(function(m) {
-        t.equal(m.length, 2)
-        times += 1
-    })
-    observer.observeObject(object)
-    action2()
-    t.equal(times, 1)
-    observer.destroy()
-    t.end()
-})
-
-test('allInOne as action', function(t) {
-    var times = 0
-    var observer = dop.createObserver(function(m) {
-        t.equal(m.length, 6)
-        times += 1
-    })
-    observer.observeObject(object)
-    allInOne()
-    t.equal(times, 1)
-    observer.destroy()
-    t.end()
-})
-
-test('allInOne as collector', function(t) {
-    var times = 0
-    var observer = dop.createObserver(function(m) {
-        t.equal(m.length, 6)
-        times += 1
-    })
-    observer.observeObject(object)
-    allInOneCollector()
-    t.equal(times, 1)
-    observer.destroy()
-    t.end()
-})
diff --git a/test/computeds.js b/test/computeds.js
deleted file mode 100644
index 90e2f37..0000000
--- a/test/computeds.js
+++ /dev/null
@@ -1,658 +0,0 @@
-var test = require('tape')
-// require('tabe').createStream( test );
-var dop = require('./.proxy').create()
-var computed = dop.computed
-var get = dop.get
-var set = dop.set
-var del = dop.del
-
-test('Creating inside register', function(t) {
-    var collector = dop.collect()
-    var runs = 0
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez',
-        fullname: computed(function(oldvalue) {
-            t.equal(oldvalue, undefined)
-            runs += 1
-            return get(this, 'name') + ' ' + get(this, 'surname')
-        })
-    })
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    t.equal(object.fullname, 'Josema Gonzalez')
-    t.equal(runs, 1)
-
-    t.equal(collector.mutations.length, 0, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Setting a computed', function(t) {
-    var collector = dop.collect()
-    var runs = 0
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez',
-        fullname: '...'
-    })
-    set(
-        object,
-        'fullname',
-        computed(function(oldvalue) {
-            t.equal(oldvalue, '...')
-            runs += 1
-            return get(this, 'name') + ' ' + get(this, 'surname')
-        })
-    )
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    t.equal(object.fullname, 'Josema Gonzalez')
-    t.equal(runs, 1)
-
-    t.equal(collector.mutations.length, 1, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('addComputed', function(t) {
-    var collector = dop.collect()
-    var runs = 0
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez',
-        fullname: '...'
-    })
-    dop.set(
-        object,
-        'fullname',
-        dop.computed(function(oldvalue) {
-            t.equal(oldvalue, '...')
-            runs += 1
-            return get(this, 'name') + ' ' + get(this, 'surname')
-        })
-    )
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    t.equal(object.fullname, 'Josema Gonzalez')
-    t.equal(runs, 1)
-
-    t.equal(collector.mutations.length, 1, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Mutating name', function(t) {
-    var collector = dop.collect()
-    var runs = 0
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez',
-        fullname: computed(function(oldvalue) {
-            runs += 1
-            return get(this, 'name') + ' ' + get(this, 'surname')
-        })
-    })
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    set(object, 'name', 'Enzo')
-    t.equal(object.fullname, 'Enzo Gonzalez')
-    t.equal(runs, 2)
-
-    t.equal(collector.mutations.length, 2, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Computed based in other computed', function(t) {
-    var collector = dop.collect()
-    var runs = 0
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez',
-        fullname: computed(function(oldvalue) {
-            runs += 1
-            return get(this, 'name') + ' ' + get(this, 'surname')
-        }),
-        fullnameUpper: computed(function(oldvalue) {
-            runs += 1
-            return get(this, 'fullname').toUpperCase()
-        })
-    })
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    t.equal(object.fullnameUpper, 'JOSEMA GONZALEZ')
-    set(object, 'name', 'Enzo')
-    t.equal(object.fullname, 'Enzo Gonzalez')
-    t.equal(object.fullnameUpper, 'ENZO GONZALEZ')
-    t.equal(runs, 4)
-
-    t.equal(collector.mutations.length, 3, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Computed based in other object', function(t) {
-    var collector = dop.collect()
-    var runs = 0
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez'
-    })
-    var object2 = dop.register({
-        fullname: computed(function(oldvalue) {
-            t.equal(oldvalue, undefined)
-            runs += 1
-            return get(object, 'name') + ' ' + get(object, 'surname')
-        })
-    })
-
-    t.equal(object2.fullname, 'Josema Gonzalez')
-    t.equal(object2.fullname, 'Josema Gonzalez')
-    t.equal(runs, 1)
-
-    t.equal(collector.mutations.length, 0, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Computed based in other object computed', function(t) {
-    var collector = dop.collect()
-    var runs = 0
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez'
-    })
-    var object2 = dop.register({
-        fullname: computed(function(oldvalue) {
-            runs += 1
-            return get(object, 'name') + ' ' + get(object, 'surname')
-        })
-    })
-    var object3 = dop.register({
-        fullnameUpper: computed(function(oldvalue) {
-            runs += 1
-            return get(object2, 'fullname').toUpperCase()
-        })
-    })
-
-    t.equal(object2.fullname, 'Josema Gonzalez')
-    t.equal(object2.fullname, 'Josema Gonzalez')
-    t.equal(object3.fullnameUpper, 'JOSEMA GONZALEZ')
-    t.equal(object3.fullnameUpper, 'JOSEMA GONZALEZ')
-    set(object, 'name', 'Enzo')
-    t.equal(object2.fullname, 'Enzo Gonzalez')
-    t.equal(object2.fullname, 'Enzo Gonzalez')
-    t.equal(object3.fullnameUpper, 'ENZO GONZALEZ')
-    t.equal(object3.fullnameUpper, 'ENZO GONZALEZ')
-    t.equal(runs, 4)
-
-    t.equal(collector.mutations.length, 3, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Deep property', function(t) {
-    var collector = dop.collect()
-    var object = dop.register({
-        subobject: {
-            name: 'Josema',
-            surname: 'Gonzalez'
-        },
-        fullname: computed(function(oldvalue) {
-            t.equal(oldvalue, undefined)
-            return (
-                get(this.subobject, 'name') +
-                ' ' +
-                get(this.subobject, 'surname')
-            )
-        })
-    })
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    t.equal(collector.mutations.length, 0, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Deep computed set', function(t) {
-    var collector = dop.collect()
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez'
-    })
-
-    set(object, 'subobject', {
-        fullname: computed(function(oldvalue) {
-            t.equal(oldvalue, undefined)
-            return get(object, 'name') + ' ' + get(object, 'surname')
-        })
-    })
-
-    t.equal(object.subobject.fullname, 'Josema Gonzalez')
-    t.equal(collector.mutations.length, 1, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Deep computed addComputed', function(t) {
-    var collector = dop.collect()
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez',
-        subobject: {}
-    })
-    dop.set(
-        object.subobject,
-        'fullname',
-        dop.computed(function(oldvalue) {
-            t.equal(oldvalue, undefined)
-            return get(object, 'name') + ' ' + get(object, 'surname')
-        })
-    )
-
-    t.equal(object.subobject.fullname, 'Josema Gonzalez')
-    t.equal(collector.mutations.length, 1, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Deep computed second object', function(t) {
-    var collector = dop.collect()
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez',
-        subobject: {}
-    })
-    var object2 = dop.register({
-        subobject: {
-            fullname: computed(function(oldvalue) {
-                t.equal(oldvalue, undefined)
-                return get(object, 'name') + ' ' + get(object, 'surname')
-            })
-        }
-    })
-
-    t.equal(object2.subobject.fullname, 'Josema Gonzalez')
-    t.equal(collector.mutations.length, 0, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Deleting derivation', function(t) {
-    var collector = dop.collect()
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez',
-        fullname: computed(function(oldvalue) {
-            return get(this, 'name') + ' ' + get(this, 'surname')
-        })
-    })
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    del(object, 'name')
-    t.equal(object.fullname, 'undefined Gonzalez')
-    t.equal(collector.mutations.length, 2, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Deleting derivation', function(t) {
-    var collector = dop.collect()
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez',
-        fullname: computed(function(oldvalue) {
-            return get(this, 'name') + ' ' + get(this, 'surname')
-        })
-    })
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    del(object, 'fullname')
-    t.equal(object.fullname, undefined)
-    t.equal(collector.mutations.length, 1, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Deleting parent object where derivation is instantiated', function(t) {
-    var collector = dop.collect()
-    var object = dop.register({
-        subobject: {
-            name: 'Josema',
-            surname: 'Gonzalez'
-        },
-        fullname: computed(function(oldvalue) {
-            try {
-                return (
-                    get(this.subobject, 'name') +
-                    ' ' +
-                    get(this.subobject, 'surname')
-                )
-            } catch (e) {
-                t.equal(
-                    e.toString().indexOf('TypeError') > -1,
-                    true,
-                    'Is a typeError'
-                )
-            }
-        })
-    })
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    del(object, 'subobject')
-
-    t.equal(collector.mutations.length, 2, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Creating computed from derivations deleted', function(t) {
-    var collector = dop.collect()
-    var object = dop.register({
-        subobject: {
-            name: 'Josema',
-            surname: 'Gonzalez'
-        }
-    })
-    var copy = object.subobject
-    set(copy, 'lol', { test: 1234 })
-    del(object, 'subobject')
-    set(
-        object,
-        'fullname',
-        computed(function() {
-            return get(copy, 'name') + ' ' + get(copy, 'surname')
-        })
-    )
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    set(copy, 'name', 'Enzo')
-    t.equal(object.fullname, 'Josema Gonzalez')
-
-    t.equal(collector.mutations.length, 3, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Creating computed from other computed deleted', function(t) {
-    var collector = dop.collect()
-    var object = dop.register({
-        subobject: {
-            name: 'Josema',
-            surname: 'Gonzalez'
-        }
-    })
-
-    var copy = object.subobject
-    del(object, 'subobject')
-    var object2 = dop.register({
-        fullname: computed(function() {
-            return get(copy, 'name') + ' ' + get(copy, 'surname')
-        })
-    })
-
-    t.equal(object2.fullname, 'Josema Gonzalez')
-    set(copy, 'name', 'Enza')
-    t.equal(object2.fullname, 'Josema Gonzalez')
-    set(object, 'subobject', copy)
-    set(object.subobject, 'name', 'Enzo')
-    t.equal(object2.fullname, 'Enzo Gonzalez')
-
-    t.equal(collector.mutations.length, 5, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Arrays', function(t) {
-    var collector = dop.collect()
-
-    var object = dop.register({
-        array: [{ val: 'Josema' }, { val: 'Gonzalez' }],
-        fullname: computed(function() {
-            return get(this.array[0], 'val') + ' ' + get(this.array[1], 'val')
-        })
-    })
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    object.array.reverse()
-    t.equal(object.fullname, 'Gonzalez Josema')
-
-    t.equal(collector.mutations.length, 2, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Arrays2', function(t) {
-    var collector = dop.collect()
-
-    var object = dop.register({
-        array: [{ val: 'Josema' }, { val: 'Gonzalez' }],
-        fullname: computed(function() {
-            return get(this.array[0], 'val') + ' ' + get(this.array[1], 'val')
-        }),
-        fullnameReverse: computed(function() {
-            return get(this, 'fullname').toUpperCase()
-        })
-    })
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    object.array.reverse()
-    t.equal(object.fullnameReverse, 'GONZALEZ JOSEMA')
-
-    t.equal(collector.mutations.length, 3, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Arrays 3', function(t) {
-    var collector = dop.collect()
-
-    var object = dop.register({
-        array: [{ val: 'Josema' }, { val: 'Gonzalez' }],
-        fullname: computed(function() {
-            return get(this.array[0], 'val') + ' ' + get(this.array[1], 'val')
-        })
-    })
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    set(object.array, 0, { val: 'Enzo' })
-    set(object.array, 2, null)
-    t.equal(object.fullname, 'Enzo Gonzalez')
-
-    t.equal(collector.mutations.length, 4, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Multiple computed same property', function(t) {
-    var collector = dop.collect()
-
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez',
-        fullname: computed(function() {
-            return get(this, 'name') + ' ' + get(this, 'surname')
-        })
-    })
-
-    t.equal(object.fullname, 'Josema Gonzalez')
-    set(object, 'name', 'Enzo')
-    t.equal(object.fullname, 'Enzo Gonzalez')
-    dop.set(
-        object,
-        'fullname',
-        dop.computed(function() {
-            return get(this, 'name').toUpperCase()
-        })
-    )
-    set(object, 'name', 'Enza')
-    t.equal(object.fullname, 'ENZA')
-    set(object, 'surname', 'Hernandez')
-    t.equal(object.fullname, 'Enza Hernandez')
-
-    t.equal(collector.mutations.length, 8, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Removing computeds', function(t) {
-    var collector = dop.collect()
-    function computed1() {
-        return get(this, 'name') + ' ' + get(this, 'surname')
-    }
-    function computed2() {
-        return get(this, 'name').toUpperCase()
-    }
-
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez',
-        fullname: computed(computed1)
-    })
-    dop.set(object, 'fullname', dop.computed(computed2))
-
-    t.equal(object.fullname, 'JOSEMA')
-    set(object, 'surname', 'Hernandez')
-    t.equal(object.fullname, 'Josema Hernandez')
-
-    dop.removeComputed(object, 'fullname', computed1)
-    set(object, 'name', 'Enzo')
-    t.equal(object.fullname, 'ENZO')
-    dop.removeComputed(object, 'fullname', computed2)
-    set(object, 'name', 'Enzolino')
-    t.equal(object.fullname, 'ENZO')
-
-    t.equal(collector.mutations.length, 6, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Removing all computeds', function(t) {
-    var collector = dop.collect()
-    function computed1() {
-        return get(this, 'name') + ' ' + get(this, 'surname')
-    }
-    function computed2() {
-        return get(this, 'name').toUpperCase()
-    }
-
-    var object = dop.register({
-        name: 'Josema',
-        surname: 'Gonzalez',
-        fullname: computed(computed1)
-    })
-    dop.set(object, 'fullname', dop.computed(computed2))
-
-    t.equal(object.fullname, 'JOSEMA')
-    set(object, 'surname', 'Hernandez')
-    t.equal(object.fullname, 'Josema Hernandez')
-
-    dop.removeComputed(object, 'fullname')
-    set(object, 'name', 'Enzo')
-    t.equal(object.fullname, 'Josema Hernandez')
-
-    t.equal(collector.mutations.length, 4, 'mutations')
-    collector.emit()
-    t.end()
-})
-
-test('Update computeds', function(t) {
-    var collector = dop.collect()
-    var object = dop.register({
-        todos: [],
-        completedCount: computed(function() {
-            return get(this, 'todos').reduce(function(sum, todo) {
-                return sum + (get(todo, 'completed') ? 1 : 0)
-            }, 0)
-        }),
-        completedAll: computed(function() {
-            return get(this, 'todos').length - get(this, 'completedCount')
-        })
-    })
-
-    t.equal(object.completedCount, 0)
-    t.equal(object.completedAll, 0)
-
-    object.todos.push({ completed: false })
-    object.todos.push({ completed: true })
-    object.todos.push({ completed: false })
-
-    t.equal(object.completedCount, 1)
-    t.equal(object.completedAll, 2)
-
-    set(object.todos[0], 'completed', true)
-
-    t.equal(object.completedCount, 2)
-    t.equal(object.completedAll, 1)
-
-    set(object.todos[2], 'completed', true)
-
-    t.equal(object.completedCount, 3)
-    t.equal(object.completedAll, 0)
-
-    // t.equal(collector.mutations.length, 4, "mutations")
-    collector.emit()
-    t.end()
-})
-
-test('Computed must listen mutations even if property is not defined (objects)', function(t) {
-    var object = dop.register({
-        items: {},
-        totalItems: computed(function() {
-            return Object.keys(get(this, 'items')).length
-        })
-    })
-
-    t.equal(object.totalItems, 0)
-    set(object.items, 'demo', true)
-    set(object.items, 'demo2', true)
-    t.equal(object.totalItems, 2)
-
-    t.end()
-})
-
-test('Computed must listen mutations even if property is not defined (arrays)', function(t) {
-    var object = dop.register({
-        items: [],
-        totalItems: computed(function() {
-            return get(this, 'items').length
-        })
-    })
-
-    var collector = dop.collect()
-    t.equal(object.totalItems, 0)
-    t.equal(collector.mutations.length, 0)
-    set(object.items, 0, true)
-    t.equal(collector.mutations.length, 3)
-    object.items.push(true)
-    set(object.items, 'demo2', true)
-    t.equal(object.totalItems, 2)
-    t.equal(collector.mutations.length, 6)
-    collector.emit()
-
-    t.end()
-})
-
-test('Using get syntax', function(t) {
-    var object = dop.register({
-        items: [],
-        get totalItems() {
-            return get(this, 'items').length
-        }
-    })
-
-    var collector = dop.collect()
-    t.equal(object.totalItems, 0)
-    t.equal(collector.mutations.length, 0)
-    set(object.items, 0, true)
-    t.equal(collector.mutations.length, 3)
-    t.equal(object.totalItems, 1)
-    object.items.push(true)
-    set(object.items, 'demo2', true)
-    t.equal(object.totalItems, 2)
-    t.equal(collector.mutations.length, 6)
-    collector.emit()
-
-    t.end()
-})
diff --git a/test/create_node.js b/test/create_node.js
new file mode 100644
index 0000000..90759b5
--- /dev/null
+++ b/test/create_node.js
@@ -0,0 +1,326 @@
+import test from 'ava'
+import { createNode } from '../src'
+import { RPC_CREATOR } from '../src/const'
+
+test('Api', async (t) => {
+    const server = createNode()
+    const client = createNode()
+
+    client.open(server.message, (a, b, req) => {
+        t.is(a, 2)
+        t.is(b, 5)
+        t.true(req instanceof Promise)
+        t.deepEqual(Object.keys(req).length, 3)
+        t.is(req.node, client)
+        t.is(typeof req.resolve, 'function')
+        t.is(typeof req.reject, 'function')
+    })
+    const callClient = server.open(client.message)
+    const promise = callClient(2, 5)
+
+    t.deepEqual(Object.keys(server).length, 6)
+
+    t.deepEqual(Object.keys(promise).length, 5)
+    t.true(promise instanceof Promise)
+    t.is(promise.node, server)
+    t.is(typeof server.send, 'function')
+    t.is(typeof promise.resolve, 'function')
+    t.is(typeof promise.reject, 'function')
+    t.is(typeof promise.createdAt, 'number')
+})
+
+test('Checking api req', async (t) => {
+    const server = createNode()
+    const client = createNode()
+    server.open(client.message, (...args) => {
+        t.is(args.length, 1)
+        t.deepEqual(Object.keys(args[0]), ['resolve', 'reject', 'node'])
+    })
+    const callServer = client.open(server.message)
+    callServer()
+})
+
+test('Callback pattern example', async (t) => {
+    const server = createNode()
+    const client = createNode()
+
+    // server side
+    server.open(client.message, (a, b, callback) => {
+        callback(a * b)
+    })
+
+    // client side
+    const callServer = client.open(server.message)
+    callServer(3, 3, (value) => {
+        t.is(value, 9)
+    })
+})
+
+test('Calling a defined function .message must return true', async (t) => {
+    const server = createNode()
+    const client = createNode()
+    const callClient = server.open(client.message, () => {})
+    const callServer = client.open((msg) => {
+        t.is(server.message(msg), true)
+    })
+    callServer()
+})
+
+test('Calling a not defined function .message must return false', async (t) => {
+    const server = createNode()
+    const client = createNode()
+    const callClient = server.open(client.message)
+    const callServer = client.open((msg) => {
+        t.is(server.message(msg), false)
+    })
+    callServer()
+})
+
+test('Response (reject or resolve must delete request)', async (t) => {
+    const server = createNode()
+    const client = createNode()
+    server.open(client.message, (v) => {})
+    const callServer = client.open(server.message)
+    t.is(Object.keys(client.requests).length, 0)
+    callServer().then((v) => {
+        t.is(Object.keys(client.requests).length, 0)
+    })
+    t.is(Object.keys(client.requests).length, 1)
+})
+
+test('Passing same functions should not create a new one', async (t) => {
+    function rpcFilter() {
+        const rpcs = {}
+        return ({ rpc, function_id }) => {
+            if (rpcs.hasOwnProperty(function_id)) {
+                return rpcs[function_id]
+            } else {
+                rpcs[function_id] = rpc
+                return rpc
+            }
+        }
+    }
+
+    const server = createNode()
+    const client = createNode({ rpcFilter: rpcFilter() })
+
+    // Client side
+    const callServer = client.open(
+        server.message,
+        (repeated1, repeated2, callserverasargument) => {
+            t.is(repeated1, repeated2)
+            t.is(callServer, callserverasargument)
+        }
+    )
+
+    // Server side
+    const receiveFromClient = () => {}
+    const repeated = () => {}
+    const callClient = server.open(client.message, receiveFromClient)
+    callClient(repeated, repeated, receiveFromClient)
+})
+
+test('Testing messages', async (t) => {
+    const serdes = { serialize: (m) => m, deserialize: (m) => m }
+    const server = createNode(serdes)
+    const client = createNode(serdes)
+
+    client.open(
+        (msg) => {
+            t.deepEqual(msg, [-1, 0, 10])
+            server.message(msg)
+        },
+        (a, b) => a * b
+    )
+    const callClient = server.open((msg) => {
+        t.deepEqual(msg, [1, 0, [2, 5]])
+        client.message(msg)
+    })
+    const ten = await callClient(2, 5)
+    t.is(ten, 10)
+})
+
+test('Escaping $r', async (t) => {
+    const serdes = { serialize: (m) => m, deserialize: (m) => m }
+    const server = createNode(serdes)
+    const client = createNode(serdes)
+
+    // server side
+    server.open(
+        (msg) => {
+            t.deepEqual(msg, [-1, 0, { $escape: { $r: 1 } }])
+            client.message(msg)
+        },
+        (arg) => {
+            t.deepEqual(arg, { $r: 0 })
+            return { $r: 1 }
+        }
+    )
+
+    // client side
+    const callServer = client.open((msg) => {
+        t.deepEqual(msg, [1, 0, [{ $escape: { $r: 0 } }]])
+        server.message(msg)
+    })
+    const resu = await callServer({ $r: 0 })
+    t.deepEqual(resu, { $r: 1 })
+})
+
+test('Using resolve', async (t) => {
+    const server = createNode()
+    const client = createNode()
+    server.open(client.message)
+    t.is(Object.keys(client.requests).length, 0)
+    const callServer = client.open(server.message)
+    const req = callServer()
+    t.is(Object.keys(client.requests).length, 1)
+    req.then((value) => {
+        t.is(value, 'resolved')
+    })
+    req.resolve('resolved')
+    t.is(Object.keys(client.requests).length, 0)
+})
+
+test('Using reject', async (t) => {
+    const server = createNode()
+    const client = createNode()
+    server.open(client.message)
+    t.is(Object.keys(client.requests).length, 0)
+    const callServer = client.open(server.message)
+    const req = callServer()
+    t.is(Object.keys(client.requests).length, 1)
+    req.catch((error) => {
+        t.is(error, 'rejected')
+    })
+    req.reject('rejected')
+    t.is(Object.keys(client.requests).length, 0)
+})
+
+test('Using push', async (t) => {
+    const server = createNode()
+    const client = createNode()
+    server.open(client.message, (...args) => {
+        t.is(args.length, 1)
+        t.deepEqual(Object.keys(args[0]), ['node'])
+    })
+    const callServer = client.open(server.message)
+    t.is(Object.keys(client.requests).length, 0)
+    t.is(callServer.push(), undefined)
+    t.is(Object.keys(client.requests).length, 0)
+})
+
+test('Calling functions from client to server with another node in the middle', async (t) => {
+    // connection 1
+    const server = createNode()
+    const middleServer = createNode()
+    // connection 2
+    const middleClient = createNode()
+    const client = createNode()
+
+    // server side
+    server.open(middleServer.message, () => ({
+        multiply: (a, b) => ({
+            sum: (c, d) => c + d,
+            value: a * b,
+        }),
+    }))
+
+    // middle side
+    const callServer = middleServer.open(server.message)
+    const objectServer = await callServer()
+    middleClient.open(client.message, () => ({
+        multiply: objectServer.multiply,
+    }))
+
+    // client side
+    const callMiddle = client.open(middleClient.message)
+    const objectMiddle = await callMiddle()
+    const { value, sum } = await objectMiddle.multiply(5, 5)
+    t.is(value, 25)
+    const result = await sum(5, 5)
+    t.is(result, 10)
+})
+
+test('rpc is created if rpcFilter returns the rpc itself', async (t) => {
+    const n = Math.random()
+    const server = createNode({ rpcFilter: () => n })
+    const client = createNode()
+    const callClient = server.open(client.message, (fn) => {
+        t.is(fn, n)
+    })
+    t.is(callClient, n)
+    const callServer = client.open(server.message)
+    callServer(() => {})
+})
+
+test('rpcFilter API', async (t) => {
+    function login({ value, fn }) {
+        t.is(typeof fn, 'function')
+    }
+    function entryFunction(fn0) {
+        fn0()
+        return {
+            value: 1,
+            login,
+        }
+    }
+
+    const rpcFilter = ({
+        rpc,
+        node,
+        function_id,
+        function_creator,
+        caller,
+        path,
+    }) => {
+        // console.log({ function_id, function_creator, caller, path })
+        t.is(node, server)
+        if (function_id === 0) {
+            t.is(function_creator, RPC_CREATOR.ENTRY)
+            t.is(caller, undefined)
+            t.is(path, undefined)
+        }
+        if (function_id === 1) {
+            t.is(function_creator, RPC_CREATOR.REQUEST)
+            t.is(caller, entryFunction)
+            t.deepEqual(path, [0])
+        }
+        if (function_id === 2) {
+            t.is(function_creator, RPC_CREATOR.RESPONSE)
+            t.is(caller, undefined)
+            t.deepEqual(path, [])
+        }
+        if (function_id === 3) {
+            t.is(function_creator, RPC_CREATOR.REQUEST)
+            t.is(caller, login)
+            t.deepEqual(path, [0, 'fn'])
+        }
+        return rpc
+    }
+    const server = createNode({ rpcFilter })
+    const client = createNode()
+    server.ENV = 'SERVER'
+    client.ENV = 'CLIENT'
+    server.open(client.message, entryFunction)
+    const callServer = client.open(server.message)
+    const objServer = await callServer(() => {
+        return () => {}
+    })
+    objServer.login({ value: 2, fn: () => {} })
+})
+
+test('registerRpc & createRpc', async (t) => {
+    const server = createNode()
+    const client = createNode()
+    server.open(client.message)
+    client.open(server.message)
+
+    const number = Math.random()
+    server.registerRpc('Login', (n) => {
+        t.is(n, number)
+        return n + 1
+    })
+    const rpc = client.createRpc('Login')
+    const result = await rpc(number)
+    t.is(number + 1, result)
+})
diff --git a/test/create_store.js b/test/create_store.js
new file mode 100644
index 0000000..5bd10cb
--- /dev/null
+++ b/test/create_store.js
@@ -0,0 +1,163 @@
+import test from 'ava'
+import { createStore, TYPE } from '../src'
+
+test('createStore', function (t) {
+    const state = { number: 1 }
+    const store = createStore(state)
+
+    t.is(state, store.state)
+})
+
+// test('getState gets a deep copy', function(t) {
+//     const state = { prop: true }
+//     const store = createStore(state)
+
+//     t.not(state, store.getState())
+//     t.deepEqual(state, store.getState())
+// })
+
+test('subscribe', function (t) {
+    const state = { prop: true }
+    const store = createStore(state)
+    const listener = () => {}
+    const filter = () => {}
+    store.subscribe(listener, filter)
+    t.true(store.listeners.has(listener))
+    t.is(store.listeners.get(listener), filter)
+    t.is(store.listeners.size, 1)
+    store.subscribe(listener)
+    t.is(store.listeners.size, 1)
+    t.is(store.listeners.get(listener), undefined)
+})
+
+test('unsubscribe from subscribe', function (t) {
+    const state = { prop: true }
+    const store = createStore(state)
+    const listener = () => {}
+    const unsubscribe = store.subscribe(listener)
+    t.true(store.listeners.has(listener))
+    t.is(store.listeners.size, 1)
+    unsubscribe()
+    t.is(store.listeners.size, 0)
+    t.false(store.listeners.has(listener))
+})
+
+test('unsubscribe', function (t) {
+    const state = { prop: true }
+    const store = createStore(state)
+    const listener = () => {}
+    store.subscribe(listener)
+    t.true(store.listeners.has(listener))
+    t.is(store.listeners.size, 1)
+    store.unsubscribe(listener)
+    t.is(store.listeners.size, 0)
+    t.false(store.listeners.has(listener))
+})
+
+test('applyPatch', function (t) {
+    const state = { prop: false }
+    const store = createStore(state)
+    const patch = { prop: true }
+    const listener = (p) => {}
+    store.subscribe(listener)
+    const outputs = store.applyPatch(patch)
+    t.true(Array.isArray(outputs))
+    t.is(outputs.length, 1)
+    t.is(outputs[0].listener, listener)
+    t.not(outputs[0].patch, patch)
+    t.deepEqual(outputs[0].patch, patch)
+    t.deepEqual(outputs[0].unpatch, { prop: false })
+    t.true(Array.isArray(outputs[0].mutations))
+
+    t.deepEqual(outputs.patch, patch)
+    t.deepEqual(outputs.unpatch, { prop: false })
+    t.deepEqual(outputs.mutations, outputs[0].mutations)
+    t.true(Array.isArray(outputs.mutations))
+})
+
+test('applyPatch with function', function (t) {
+    const state = { prop: false }
+    const store = createStore(state)
+    const listener = (p) => {}
+    store.subscribe(listener)
+    const outputs = store.applyPatch((draft) => {
+        draft.prop = true
+    })
+    t.true(Array.isArray(outputs))
+    t.is(outputs.length, 1)
+    t.is(outputs[0].listener, listener)
+    t.deepEqual(outputs[0].unpatch, { prop: false })
+    t.true(Array.isArray(outputs[0].mutations))
+
+    const { unpatch, mutations } = outputs
+    t.deepEqual(unpatch, { prop: false })
+    t.true(Array.isArray(mutations))
+    t.deepEqual(mutations, outputs[0].mutations)
+})
+
+test('subscribe filter', function (t) {
+    const initialstate = { prop: false }
+    const store = createStore(initialstate)
+    const patch = { prop: true, newprop: true }
+    const filter = (mutation) => mutation.prop !== 'newprop'
+    store.subscribe(() => {}, filter)
+    const result = store.applyPatch(patch)
+    const [output] = result
+    t.not(output.patch, patch)
+    t.deepEqual(output.patch, { prop: true })
+    t.deepEqual(output.unpatch, { prop: false })
+    t.is(output.mutations.length, 1)
+    t.is(result.mutations.length, 2)
+})
+
+test('subscribe output of applyPatch must return same length that listeners', function (t) {
+    const store = createStore({ prop: false })
+    const patch = { prop: true, newprop: true }
+    store.subscribe(
+        () => {},
+        () => false
+    )
+    store.subscribe(
+        () => {},
+        () => true
+    )
+    const output = store.applyPatch(patch)
+    t.is(output.length, 2)
+    t.deepEqual(output[0].patch, {})
+    t.deepEqual(output[1].patch, patch)
+    t.deepEqual(output[0].mutations.length, 0)
+    t.deepEqual(output[1].mutations.length, 2)
+})
+
+test('filtered patches by createPatchAndUnpatchFromMutations must be the same as original patch', function (t) {
+    const store = createStore({ params: { a: 1, b: 2 } })
+    const patch = { params: TYPE.Replace({ c: 3 }) }
+    store.subscribe(
+        () => {},
+        () => false
+    )
+    store.subscribe(
+        () => {},
+        () => true
+    )
+    const output = store.applyPatch(patch)
+    t.is(output.length, 2)
+    t.deepEqual(output[0].patch, {})
+    t.deepEqual(output[1].patch, patch)
+})
+
+test('Matching subscriber patch and original patch', function (t) {
+    const store = createStore({
+        ['one']: {},
+    })
+    store.subscribe((patch) => {
+        t.deepEqual(patch, { one: { two: TYPE.Replace({ three: true }) } })
+    })
+
+    const listeners = store.applyPatch((d) => {
+        d['one']['two'] = { three: true }
+    })
+
+    t.deepEqual(listeners[0].patch, listeners.patch)
+    t.deepEqual(store.state, { one: { two: { three: true } } })
+})
diff --git a/test/encode_decode.js b/test/encode_decode.js
deleted file mode 100644
index 2964f15..0000000
--- a/test/encode_decode.js
+++ /dev/null
@@ -1,35 +0,0 @@
-var test = require('tape')
-// require('tabe').createStream( test );
-var dop = require('../dist/dop')
-
-var data = {
-    string: 'string',
-    boolean: true,
-    number: -123,
-    Infinity: -Infinity,
-    float: 1.234153454354341,
-    long: 12313214234312324353454534534,
-    undefined: undefined,
-    null: null,
-    regexp: /molamazo/g,
-    date: new Date(),
-    '~DOP': 'Mola',
-    und: '~U',
-    inf: '~I',
-    fun: '~F',
-    nan: '~N',
-    reg: '~R'
-}
-
-test('Multiple data', function(t) {
-    var encoded = dop.encode(data)
-    var decoded = dop.decode(encoded)
-    t.deepEqual(decoded, data, 'deepEqual')
-    // data.NaN = NaN;
-    data.function = function() {}
-    decoded = dop.decode(dop.encode(data, dop.core.encodeFunction))
-    t.equal(JSON.stringify(decoded), JSON.stringify(data), 'Stringify')
-    t.equal(typeof decoded.function, 'function', 'Has function')
-    // console.log( decoded);
-    t.end()
-})
diff --git a/test/getobjectpath.js b/test/getobjectpath.js
deleted file mode 100644
index bda8936..0000000
--- a/test/getobjectpath.js
+++ /dev/null
@@ -1,178 +0,0 @@
-var test = require('tape');
-var dop = require('./.proxy').create();
-
-
-
-var object_id = 1
-var object = dop.register({
-    array: [[[]]],
-    obj: {obj1:{obj2:{}}}
-})
-
-
-test('Object: defaults', function(t) {
-
-    var path
-
-    path = dop.getObjectPath(object)
-    t.deepEqual(path,[object_id], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.obj)
-    t.deepEqual(path,[object_id,"obj"], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.obj.obj1)
-    t.deepEqual(path,[object_id,"obj","obj1"], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.obj.obj1.obj2)
-    t.deepEqual(path,[object_id,"obj","obj1","obj2"], JSON.stringify(path))
-
-    t.end()
-});
-
-
-test('Object: delete last', function(t) {
-
-    var objectStored = object.obj.obj1.obj2
-    delete object.obj.obj1.obj2
-
-    var path
-
-    path = dop.getObjectPath(object)
-    t.deepEqual(path,[object_id], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.obj)
-    t.deepEqual(path,[object_id,"obj"], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.obj.obj1)
-    t.deepEqual(path,[object_id,"obj","obj1"], JSON.stringify(path))
-
-    path = dop.getObjectPath(objectStored)
-    t.deepEqual(path,undefined)
-
-    t.end()
-});
-
-
-test('Object: delete first', function(t) {
-
-    var objectStored = object.obj
-    delete object.obj
-
-    var path
-
-    path = dop.getObjectPath(object)
-    t.deepEqual(path,[object_id], JSON.stringify(path))
-
-    path = dop.getObjectPath(objectStored)
-    t.deepEqual(path,undefined)
-
-    path = dop.getObjectPath(objectStored.obj1)
-    t.deepEqual(path,undefined)
-
-    t.end()
-});
-
-
-
-test('Array: defaults', function(t) {
-
-    var path
-
-    path = dop.getObjectPath(object)
-    t.deepEqual(path,[object_id], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.array)
-    t.deepEqual(path,[object_id,"array"], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.array[0])
-    t.deepEqual(path,[object_id,"array",0], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.array[0][0])
-    t.deepEqual(path,[object_id,"array",0,0], JSON.stringify(path))
-
-    t.end()
-});
-
-
-
-test('Array: unshift', function(t) {
-
-    object.array[0].unshift(null)
-    object.array.unshift(null)
-
-    var path
-    path = dop.getObjectPath(object)
-    t.deepEqual(path,[object_id], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.array)
-    t.deepEqual(path,[object_id,"array"], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.array[1])
-    t.deepEqual(path,[object_id,"array",1], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.array[1][1])
-    t.deepEqual(path,[object_id,"array",1,1], JSON.stringify(path))
-
-    t.end()
-});
-
-
-
-
-test('Array: unshift 2', function(t) {
-
-    object.array.unshift(null)
-    object.array[2].unshift(null)
-
-    var path
-    path = dop.getObjectPath(object)
-    t.deepEqual(path,[object_id], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.array)
-    t.deepEqual(path,[object_id,"array"], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.array[2])
-    t.deepEqual(path,[object_id,"array",2], JSON.stringify(path))
-
-    path = dop.getObjectPath(object.array[2][2])
-    t.deepEqual(path,[object_id,"array",2,2], JSON.stringify(path))
-
-    t.end()
-});
-
-test('Array: removing array', function(t) {
-
-    // object.array.unshift(null)
-    var arrayStored = object.array[2][2]
-    object.array[2].splice(0,3)
-
-    var path
-    path = dop.getObjectPath(object.array[2])
-    t.deepEqual(path,[object_id,"array",2], JSON.stringify(path))
-
-    path = dop.getObjectPath(arrayStored)
-    t.deepEqual(path, undefined)
-
-    t.end()
-});
-
-
-test('Array: removing array 2', function(t) {
-
-
-    var path
-    path = dop.getObjectPath(object.array[2])
-    t.deepEqual(path,[object_id,"array",2], JSON.stringify(path))
-
-    dop.set(object.array[2],0,[])
-
-    path = dop.getObjectPath(object.array[2][0])
-    t.deepEqual(path,[object_id,"array",2,0], JSON.stringify(path))
-
-    var arrayStored = object.array[2][0]
-    object.array.splice(2,1)
-    path = dop.getObjectPath(arrayStored)
-    t.deepEqual(path, undefined)
-
-    t.end()
-});
diff --git a/test/getpatchs.js b/test/getpatchs.js
deleted file mode 100644
index 924f036..0000000
--- a/test/getpatchs.js
+++ /dev/null
@@ -1,256 +0,0 @@
-var test = require('tape');
-var dop = require('./.proxy').create()
-var set = dop.set;
-var del = dop.del;
-var encode = dop.encode;
-var decode = dop.decode;
-
-
-
-function getPatch(collector) {
-    var patch = dop.core.getPatch(collector.mutations);
-    var patchServer = decode(encode(patch));
-    collector.destroy();
-    for (var id in patchServer)
-        return patchServer[id].chunks;
-}
-
-
-function maketest(t, patchGenerated, patchExpected, checkEncode) {
-    console.log( '###', encode(patchGenerated) );
-    t.deepEqual(patchGenerated, patchExpected, 'deepEqual');
-    if (checkEncode!==false)
-    t.equal(encode(patchGenerated), encode(patchExpected), 'equal');
-    console.log( '' );
-    t.end();
-}
-
-var header = '--- ';
-
-
-test(header+'Adding property', function(t) {
-    var objectServer = dop.register({});
-    var patchExpected = [{one:1}];
-    var mutationsExpected = 1;
-
-
-    var collector = dop.collect();
-    set(objectServer, 'one', 1);
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-test(header+'Changing property', function(t) {
-    var objectServer = dop.register({one:1});
-    var patchExpected = [{one:11}];
-    var mutationsExpected = 1;
-
-    var collector = dop.collect();
-    set(objectServer, 'one', 11);
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-test(header+'Changing property with the same value', function(t) {
-    var objectServer = dop.register({one:11});
-    var patchExpected = undefined;
-    var mutationsExpected = 0;
-
-    var collector = dop.collect();
-    set(objectServer, 'one', 11);
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-test(header+'Deleting property', function(t) {
-    var objectServer = dop.register({one:11});
-    var patchExpected = [{one:[0]}];
-    var mutationsExpected = 1;
-
-    var collector = dop.collect();
-    del(objectServer, 'one');
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-test(header+'Change and delete a removed item', function(t) {
-    var objectServer = dop.register({one:11});
-    var patchExpected = [{two:2,one:[0]}];
-    var mutationsExpected = 3;
-
-    var collector = dop.collect();
-    set(objectServer, 'one', 'Changeddd');
-    del(objectServer, 'one');
-    set(objectServer, 'two', 2);
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected, false);
-});
-
-
-test(header+'Setting property array', function(t) {
-    var object = dop.register({array:[1,2]});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":{"5":"three","length":6}}];
-    var mutationsExpected = 2;
-    set(object.array, 5, "three");
-
-
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-
-test(header+'Adding undefineds', function(t) {
-    var objectServer = dop.register({one:1,array:[]});
-    var patchExpected = [{array: [ 2, [ 0, 0, undefined ] ], one:undefined,two:undefined}];
-    var mutationsExpected = 3;
-
-
-    var collector = dop.collect();
-    set(objectServer, 'one', undefined);
-    set(objectServer, 'two', undefined);
-    objectServer.array.push(undefined)
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-
-
-
-
-
-
-test(header+'Setting an array and mutating it', function(t) {
-    var object = dop.register({});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":[1,["c","b",{"B1":"string"},false,true]]}];
-    var mutationsExpected = 5;
-    set(object, 'array', [true,false]);
-    object.array.push('a','b','c')
-    set(object.array, 2, {B1:[true,false]});
-    object.array.reverse()
-    set(object.array[2], 'B1', 'string');
-
-
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-
-
-
-
-test(header+'Mutating array then mutating nested objects', function(t) {
-    var object = dop.register({array:[true,false]});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":[3,[0,1]]},{"array":{"2":[1,{"B1":false}],"length":3}}];
-    var mutationsExpected = 3;
-    object.array.reverse();
-    set(object.array, 2, {B1:false});
-
-
-
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-
-
-
-test(header+'Mutating nested objects then mutating parent array', function(t) {
-    var object = dop.register({array:[true,false]});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":{"2":[1,{"B1":false}],"length":3}},{"array":[3,[0,2]]}];
-    var mutationsExpected = 3;
-    set(object.array, 2, {B1:false});
-    object.array.reverse();
-
-
-
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-
-
-
-test(header+'Mutating array twice', function(t) {
-    var object = dop.register({array:[3,1,2]});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":[[2,[3,0,5,4,6]],[3,[0,1,1,2,3,4]]]}];
-    var mutationsExpected = 2;
-    object.array.push(5,4,6);
-    object.array.sort();
-
-
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-
-})
-
-
-
-
-test(header+'Mutating array and mutating array deeper', function(t) {
-    var object = dop.register({array:[true,false,[true,false]]});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":[3,[0,2]]},{"array":{"0":[3,[0,1]]}}];
-    var mutationsExpected = 2;
-    object.array.reverse();
-    object.array[0].reverse();
-
-
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-
-})
-
-
-
-test(header+'Mutating array deeper and mutating container', function(t) {
-    var object = dop.register({array:[true,false,[true,false]]});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":{"2":[3,[0,1]]}},{"array":[3,[0,2]]}];
-    var mutationsExpected = 2;
-    object.array[2].reverse();
-    object.array.reverse();
-
-
-    var patchGenerated = getPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-})
diff --git a/test/getset.js b/test/getset.js
new file mode 100644
index 0000000..d45d577
--- /dev/null
+++ b/test/getset.js
@@ -0,0 +1,15 @@
+import test from 'ava'
+import { getDeep } from '../src/util/getset'
+
+test('getDeep', function (t) {
+    const object = { first: 1, second: { third: 4 } }
+
+    t.is(getDeep(object, []), object)
+    t.is(getDeep(object, ['']), undefined)
+    t.is(getDeep(object, ['none']), undefined)
+    t.is(getDeep(object, ['first']), object.first)
+    t.is(getDeep(object, ['second']), object.second)
+    t.is(getDeep(object, ['second', 'third']), object.second.third)
+    t.is(getDeep(object, ['second', 'third', 'none']), undefined)
+    t.is(getDeep(object, ['second', 'none']), undefined)
+})
diff --git a/test/getunpatchs.js b/test/getunpatchs.js
deleted file mode 100644
index 5ca7dfe..0000000
--- a/test/getunpatchs.js
+++ /dev/null
@@ -1,244 +0,0 @@
-var test = require('tape');
-var dop = require('./.proxy').create();
-var set = dop.set;
-var del = dop.del;
-var encode = dop.encode;
-var decode = dop.decode;
-
-
-
-function applyPatch(collector) {
-    var patch = dop.core.getUnpatch(collector.mutations);
-    var patchServer = decode(encode(patch));
-    collector.destroy();
-    for (var id in patchServer)
-        return patchServer[id].chunks;
-}
-
-
-function maketest(t, patchGenerated, patchExpected, checkEncode) {
-    console.log( '###', encode(patchGenerated) );
-    t.deepEqual(patchGenerated, patchExpected, 'deepEqual');
-    if (checkEncode!==false)
-    t.equal(encode(patchGenerated), encode(patchExpected), 'equal');
-    console.log( '' );
-    t.end();
-}
-
-var header = '--- ';
-
-
-test(header+'Adding property', function(t) {
-    var objectServer = dop.register({});
-    var patchExpected = [{one:[0]}];
-    var mutationsExpected = 1;
-
-
-    var collector = dop.collect();
-    set(objectServer, 'one', 1);
-    var patchGenerated = applyPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-test(header+'Changing property', function(t) {
-    var objectServer = dop.register({one:1});
-    var patchExpected = [{one:1}];
-    var mutationsExpected = 1;
-
-    var collector = dop.collect();
-    set(objectServer, 'one', 2);
-    var patchGenerated = applyPatch(collector);
-    t.equal(collector.mutations.length, 1, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-test(header+'Changing property with the same value', function(t) {
-    var objectServer = dop.register({one:11});
-    var patchExpected = undefined;
-    var mutationsExpected = 0;
-
-    var collector = dop.collect();
-    set(objectServer, 'one', 11);
-    var patchGenerated = applyPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-test(header+'Deleting property', function(t) {
-    var objectServer = dop.register({one:11});
-    var collector = dop.collect();
-
-    var mutationsExpected = 1;
-    var patchExpected = [{one:11}];
-    del(objectServer, 'one');
-
-
-    var patchGenerated = applyPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-test(header+'Change and delete a removed item', function(t) {
-    var objectServer = dop.register({one:11});
-    var collector = dop.collect();
-
-
-    var mutationsExpected = 3;
-    var patchExpected = [{one:11,two:[0]}];
-    set(objectServer, 'one', 'Changeddd');
-    del(objectServer, 'one');
-    set(objectServer, 'two', 2);
-
-
-    var patchGenerated = applyPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected, false);
-});
-
-
-
-
-test(header+'Setting property array', function(t) {
-    var object = dop.register({array:[1,2]});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":{"5":[0],"length":2}}];
-    var mutationsExpected = 2;
-    set(object.array, 5, "three");
-
-
-    var patchGenerated = applyPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-
-
-
-
-
-
-
-test(header+'Setting an array and mutating it', function(t) {
-    var object = dop.register({});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":[3,[2,0]]},{"array":[0]}];
-    var mutationsExpected = 4;
-    set(object, 'array', [true,false]);
-    set(object.array, 2, {B1:[true,false]});
-    object.array.reverse()
-
-
-    var patchGenerated = applyPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-
-
-
-
-test(header+'Mutating array then mutating nested objects', function(t) {
-    var object = dop.register({array:[true,false]});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":{"2":[0],"length":2}},{"array":[3,[1,0]]}];
-    var mutationsExpected = 3;
-    object.array.reverse();
-    set(object.array, 2, {B1:false});
-
-
-
-    var patchGenerated = applyPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-
-
-
-test(header+'Mutating nested objects then mutating parent array', function(t) {
-    var object = dop.register({array:[true,false]});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":[3,[2,0]]},{"array":{"2":[0],"length":2}}];
-    var mutationsExpected = 3;
-    set(object.array, 2, {B1:false});
-    object.array.reverse();
-
-
-
-    var patchGenerated = applyPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-});
-
-
-
-
-
-test(header+'Mutating array twice', function(t) {
-    var object = dop.register({array:[3,1,2]});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":[[3,[4,3,2,1,1,0]],[2,[3,3]]]}];
-    var mutationsExpected = 2;
-    object.array.push(5,4,6);
-    object.array.sort();
-
-
-    var patchGenerated = applyPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-})
-
-
-
-
-test(header+'Mutating array and mutating array deeper', function(t) {
-    var object = dop.register({array:[true,false,[true,false]]});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":{"0":[3,[1,0]]}},{"array":[3,[2,0]]}];
-    var mutationsExpected = 2;
-    object.array.reverse();
-    object.array[0].reverse();
-
-
-    var patchGenerated = applyPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-
-})
-
-
-
-test(header+'Mutating array deeper and mutating container', function(t) {
-    var object = dop.register({array:[true,false,[true,false]]});
-    var collector = dop.collect();
-    
-
-    var patchExpected = [{"array":[3,[2,0]]},{"array":{"2":[3,[1,0]]}}];
-    var mutationsExpected = 2;
-    object.array[2].reverse();
-    object.array.reverse();
-
-
-    var patchGenerated = applyPatch(collector);
-    t.equal(collector.mutations.length, mutationsExpected, 'Mutations expecteds: '+collector.mutations.length);
-    maketest(t, patchGenerated, patchExpected);
-})
diff --git a/test/interceptors.js b/test/interceptors.js
deleted file mode 100644
index 6173f73..0000000
--- a/test/interceptors.js
+++ /dev/null
@@ -1,86 +0,0 @@
-var test = require('tape')
-// require('tabe').createStream( test );
-dop = require('./.proxy').create()
-var set = dop.set
-
-var object = dop.register({ name: 'Enzo' })
-
-test('Passing wrong arguments', function(t) {
-    var disposer
-    try {
-        disposer = dop.intercept()
-    } catch (e) {
-        t.equal(
-            e.message.indexOf('needs a registered object') > -1,
-            true,
-            e.message
-        )
-    }
-
-    try {
-        disposer = dop.intercept(object)
-    } catch (e) {
-        t.equal(e.message.indexOf('needs a callback') > -1, true, e.message)
-    }
-
-    try {
-        disposer = dop.intercept(object, 'name')
-    } catch (e) {
-        t.equal(e.message.indexOf('needs a callback') > -1, true, e.message)
-    }
-
-    var disposer = dop.intercept(object, function() {})
-    t.equal(typeof disposer, 'function')
-    disposer()
-
-    disposer = dop.intercept(object, 'name', function() {})
-    t.equal(typeof disposer, 'function')
-    disposer()
-
-    t.end()
-})
-
-test('Multiple interceptors', function(t) {
-    var inc = 1
-    var disposer1 = dop.intercept(object, 'name', function(mutation) {
-        t.equal(inc++, 2)
-        return true
-    })
-    var disposer2 = dop.intercept(object, function(mutation) {
-        t.equal(inc++, 1)
-        return true
-    })
-    var disposer3 = dop.intercept(object, 'name', function(mutation) {
-        t.equal(inc++, 3)
-        return true
-    })
-    var observer = dop.createObserver(function(mutation) {
-        disposer1()
-        disposer2()
-        disposer3()
-        observer.destroy()
-        t.equal(inc++, 4, 'observer')
-        t.end()
-    })
-    observer.observeProperty(object, 'name')
-    set(object, 'name', 'John')
-})
-
-test('Second interceptor do not return true', function(t) {
-    var inc = 1
-    var disposer1 = dop.intercept(object, function(mutation) {
-        t.equal(inc++, 1)
-        return true
-    })
-    var disposer2 = dop.intercept(object, 'name', function(mutation) {
-        disposer1()
-        disposer2()
-        t.equal(inc++, 2)
-        t.end()
-    })
-    var observer = dop.createObserver(function(mutation) {
-        t.end(false, true, 'this shouldnt happen')
-    })
-    observer.observeProperty(object, 'name')
-    set(object, 'name', 'Josema')
-})
diff --git a/test/merge.js b/test/merge.js
index 96d8105..2375632 100644
--- a/test/merge.js
+++ b/test/merge.js
@@ -1,209 +1,484 @@
-var test = require('tape');
-var dop = require('./.proxy').create();
-var merge = require('lodash.merge');
-var Combinatorics = require('js-combinatorics');
-
-
-
-var objects = {}
-objects.empty = function(){return {}}
-
-objects.types = function(){return {
-    string:'string',
-    boolean:true,
-    number:-123,
-    Infinity:-Infinity,
-    float:1.234153454354341,
-    long:12313214234312324353454534534,
-    null:null,
-    // // from here breaks
-    // undefined:undefined,
-    // NaN:NaN,
-    // symbol:Symbol('sym'),
-    // date: new Date(),
-    // regexp: /molamazo/g,
-    // function: function(){console.log(arguments)}
-}}
-objects.obj1 = function(){return {
-    a: 11,
-    b: 12,
-    array: [1,2,3,{abc:123}],
-    d: {
-        d1: 13,
-        d2: {
-            d21: 123,
-            d22: {
-                d221: 12,
-                d223: { 
-                  hola: 'hola',
-                  undefined: 'undefined'
-                }
-            }
-        }
-    },
-    arrobj: ['a','b','c','d'],
-    f: 5,
-    g: 123
-}}
-objects.obj2 = function(){return {
-    b: 3,
-    c: 5,
-    obj: {lolo:111},
-    // fun: function(){},
-    arr: [1,2,3,{La:123}],
-    array: [567],
-    arrobj: {0:1,1:2},
-    d: {
-        d2: {
-            d22: {
-                d222: 25,
-                d223: {
-                  hola:'mundo',
-                //   undefined: undefined // lodash ignores undefined values
-                }
-            }
-        }
-    }
-}};
-
-var all = ['empty', 'types', 'obj1', 'obj2']
-var argsCases = [], cmb, a
-
-for (var i=1; i<all.length; i++) {
-    cmb = Combinatorics.baseN(all, i+1);
-    while(a = cmb.next()) argsCases.push(a)//argsCases.push(a, a.concat({},'string',{},1,null,undefined,new Date(),/test/,new gify(),true,{},[{}]));
-}
-
-
-test('All cases', function(t) {
-
-    for (i=0; i<argsCases.length; i++) {
-        var data1 = argsCases[i].map(function(item) {
-            return objects[item]()
-        })
-        var data2 = argsCases[i].map(function(item) {
-            return objects[item]()
-        })
-        var o1 = dop.util.merge.apply(this, data1)
-        var o2 = merge.apply(this, data2)
-        t.deepEqual(o1, o2, argsCases[i].join('(), '))
-    }
-
-    t.end()
-})
-
-
-// test(' {}, obj1() ', function(t) {
-//     var o1 = dop.util.merge({}, obj1())
-//     var o2 = merge({}, obj1())
-//     t.deepEqual(o1, o2)
-//     t.end()
-// })
-
-// test(' {}, obj2() ', function(t) {
-//     var o1 = dop.util.merge({}, obj2())
-//     var o2 = merge({}, obj2())
-//     t.deepEqual(o1, o2)
-//     t.end()
-// })
-
-
-// test(' {}, types() ', function(t) {
-//     var o1 = dop.util.merge({}, types())
-//     var o2 = merge({}, types())
-//     t.deepEqual(o1, o2)
-//     t.end()
-// })
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-// test(' {}, obj1(), obj1() ', function(t) {
-//     var o1 = dop.util.merge({}, obj1(), obj1())
-//     var o2 = merge({}, obj1(), obj1())
-//     t.deepEqual(o1, o2)
-//     t.end()
-// })
-
-// // test(' {}, obj1(), obj2() ', function(t) {
-// //     var o1 = dop.util.merge({}, obj1(), obj2())
-// //     var o2 = merge({}, obj1(), obj2())
-// //     t.deepEqual(o1, o2)
-// //     t.end()
-// // })
-
-
-// test(' {}, obj1(), types() ', function(t) {
-//     var o1 = dop.util.merge({}, obj1(), types())
-//     var o2 = merge({}, obj1(), types())
-//     t.deepEqual(o1, o2)
-//     t.end()
-// })
-
-
-
-
-
-
-
-
-// test(' {}, obj2(), obj1() ', function(t) {
-//     var o1 = dop.util.merge({}, obj2(), obj1())
-//     var o2 = merge({}, obj2(), obj1())
-//     t.deepEqual(o1, o2)
-//     t.end()
-// })
-
-// test(' {}, obj2(), obj2() ', function(t) {
-//     var o1 = dop.util.merge({}, obj2(), obj2())
-//     var o2 = merge({}, obj2(), obj2())
-//     t.deepEqual(o1, o2)
-//     t.end()
-// })
-
-
-// test(' {}, obj2(), types() ', function(t) {
-//     var o1 = dop.util.merge({}, obj2(), types())
-//     var o2 = merge({}, obj2(), types())
-//     t.deepEqual(o1, o2)
-//     t.end()
-// })
-
-
-
-
-
-
-// test(' {}, types(), obj1() ', function(t) {
-//     var o1 = dop.util.merge({}, types(), obj1())
-//     var o2 = merge({}, types(), obj1())
-//     t.deepEqual(o1, o2)
-//     t.end()
-// })
-
-// test(' {}, types(), obj2() ', function(t) {
-//     var o1 = dop.util.merge({}, types(), obj2())
-//     var o2 = merge({}, types(), obj2())
-//     t.deepEqual(o1, o2)
-//     t.end()
-// })
-
-
-// test(' {}, types(), types() ', function(t) {
-//     var o1 = dop.util.merge({}, types(), types())
-//     var o2 = merge({}, types(), types())
-//     t.deepEqual(o1, o2)
-//     t.end()
-// })
+import test from 'ava'
+import dop from '../src'
+import _ from 'lodash' // https://github.com/lodash/lodash/blob/master/test/merge.test.js
+import R from 'ramda'
+
+// https://jsperf.com/merge-challenge
+
+run(dop.merge, 'dop')
+run(_.merge, 'lodash')
+
+function run(merge, name) {
+    test(name + ': should merge `source` into `object`', function (t) {
+        var names = {
+            characters: [{ name: 'barney' }, { name: 'fred' }],
+        }
+
+        var ages = {
+            characters: [{ age: 36 }, { age: 40 }],
+        }
+
+        var heights = {
+            characters: [{ height: '5\'4"' }, { height: '5\'5"' }],
+        }
+
+        var expected = {
+            characters: [
+                { name: 'barney', age: 36, height: '5\'4"' },
+                { name: 'fred', age: 40, height: '5\'5"' },
+            ],
+        }
+
+        t.deepEqual(merge(names, ages, heights), expected)
+    })
+
+    test(name + ': must be a completly new references', function (t) {
+        var object = { obj: { a: 1 }, arr: [2] }
+        var actual = merge({}, object)
+
+        t.deepEqual(actual, object)
+        t.not(object, actual)
+        t.not(object.obj, actual.obj)
+        t.not(object.arr, actual.arr)
+    })
+
+    test(name + ': should work with four arguments', function (t) {
+        var expected = { a: 4 },
+            actual = merge({ a: 1 }, { a: 2 }, { a: 3 }, expected)
+
+        t.deepEqual(actual, expected)
+    })
+
+    test(name + ': should merge onto function `object` values', function (t) {
+        function Foo() {}
+
+        var source = { a: 1 },
+            actual = merge(Foo, source)
+
+        t.deepEqual(actual, Foo)
+        t.deepEqual(Foo.a, 1)
+    })
+
+    test(
+        name + ': should merge first source object properties to function',
+        function (t) {
+            var fn = function () {},
+                object = { prop: {} },
+                actual = merge({ prop: fn }, object)
+
+            t.deepEqual(actual, object)
+        }
+    )
+
+    test(
+        name +
+            ': should merge first and second source object properties to function',
+        function (t) {
+            var fn = function () {},
+                object = { prop: {} },
+                actual = merge({ prop: fn }, { prop: fn }, object)
+
+            t.deepEqual(actual, object)
+        }
+    )
+
+    test(
+        name + ': should not merge onto function values of sources',
+        function (t) {
+            var source1 = { a: function () {} },
+                source2 = { a: { b: 2 } },
+                expected = { a: { b: 2 } },
+                actual = merge({}, source1, source2)
+
+            t.deepEqual(actual, expected)
+            t.true(!('b' in source1.a))
+
+            actual = merge(source1, source2)
+            t.deepEqual(actual, expected)
+        }
+    )
+
+    test(name + ': should merge onto non-plain `object` values', function (t) {
+        function Foo() {}
+
+        var object = new Foo(),
+            actual = merge(object, { a: 1 })
+
+        t.deepEqual(actual, object)
+        t.deepEqual(object.a, 1)
+    })
+
+    test(name + ': should treat sparse array sources as dense', function (t) {
+        var array = [1]
+        array[2] = 3
+
+        var actual = merge([0], array),
+            expected = array.slice()
+
+        expected[1] = undefined
+
+        t.true('1' in actual)
+        t.deepEqual(actual, expected)
+    })
+
+    test(name + ': should assign `null` values', function (t) {
+        var actual = merge({ a: 1 }, { a: null })
+        t.deepEqual(actual.a, null)
+    })
+
+    test(
+        name +
+            ': should assign non array/buffer/typed-array/plain-object source values directly',
+        function (t) {
+            function Foo() {}
+
+            var values = [
+                    new Foo(),
+                    new Boolean(),
+                    new Date(),
+                    Foo,
+                    new Number(),
+                    new String(),
+                    new RegExp(),
+                ],
+                expected = _.map(values, () => true)
+
+            var actual = _.map(values, function (value) {
+                var object = merge({}, { a: value, b: { c: value } })
+                return object.a === value && object.b.c === value
+            })
+            t.deepEqual(actual, expected)
+        }
+    )
+
+    test(name + ': should not augment source objects', function (t) {
+        var source1 = { a: [{ a: 1 }] },
+            source2 = { a: [{ b: 2 }] },
+            actual = merge({}, source1, source2)
+
+        t.deepEqual(source1.a, [{ a: 1 }])
+        t.deepEqual(source2.a, [{ b: 2 }])
+        t.deepEqual(actual.a, [{ a: 1, b: 2 }])
+
+        var source1 = { a: [[1, 2, 3]] },
+            source2 = { a: [[3, 4]] },
+            actual = merge({}, source1, source2)
+
+        t.deepEqual(source1.a, [[1, 2, 3]])
+        t.deepEqual(source2.a, [[3, 4]])
+        t.deepEqual(actual.a, [[3, 4, 3]])
+    })
+
+    test(
+        name + ': should merge plain objects onto non-plain objects',
+        function (t) {
+            function Foo(object) {
+                _.assign(this, object)
+            }
+
+            var object = { a: 1 },
+                actual = merge(new Foo(), object)
+
+            t.true(actual instanceof Foo)
+            t.deepEqual(actual, new Foo(object))
+
+            actual = merge([new Foo()], [object])
+            t.true(actual[0] instanceof Foo)
+            t.deepEqual(actual, [new Foo(object)])
+        }
+    )
+
+    test(
+        name +
+            ': should skip merging when `object` and `source` are the same value',
+        function (t) {
+            var object = {},
+                pass = true
+
+            Object.defineProperty(object, 'a', {
+                configurable: true,
+                enumerable: true,
+                get: function () {
+                    pass = false
+                },
+                set: function () {
+                    pass = false
+                },
+            })
+
+            merge(object, object)
+            t.true(pass)
+        }
+    )
+
+    test(
+        name +
+            ': should convert strings to arrays when merging arrays of `source`',
+        function (t) {
+            var object = { a: 'abcde' },
+                actual = merge(object, { a: ['x', 'y', 'z'] })
+
+            t.deepEqual(actual, { a: ['x', 'y', 'z'] })
+        }
+    )
+
+    test(name + ': checking different types', function (t) {
+        const object = {
+            string: 'string',
+            boolean: true,
+            number: -123,
+            Infinity: -Infinity,
+            float: 1.234153454354341,
+            long: 12313214234312324353454534534,
+            null: null,
+            undefined: undefined,
+            NaN: NaN,
+            symbol: Symbol('sym'),
+            date: new Date(),
+            regexp: /molamazo/g,
+            f: () => {},
+            function: function () {
+                console.log(arguments)
+            },
+            obj: { lolo: 111 },
+            arr: [1, 2, 3, { La: 123 }],
+            array: [567],
+            arrobj: { 0: 1, 1: 2 },
+            d: {
+                a: 11,
+                array: [1, 2, 3, { abc: 123 }],
+                d: {
+                    d1: 13,
+                    d2: {
+                        d21: 123,
+                        d22: {
+                            d221: 12,
+                            d223: {
+                                hola: 'hola',
+                                undefined: undefined,
+                            },
+                        },
+                    },
+                },
+                arrobj: ['a', 'b', 'c', 'd'],
+                g: 123,
+                d2: {
+                    d22: {
+                        d222: 25,
+                        d223: {
+                            hola: 'mundo',
+                        },
+                    },
+                },
+            },
+        }
+
+        var actual = merge({}, object)
+        var actual_lodash = {}
+        var actual_rambda = R.merge({}, object)
+        _.merge(actual_lodash, object)
+        t.deepEqual(object, actual)
+        t.deepEqual(object, actual_lodash)
+        t.deepEqual(object, actual_rambda)
+        t.not(object.obj, actual.obj)
+        t.not(object.obj, actual_lodash.obj)
+        // t.not(object.obj, actual_rambda.obj) // this fails because Rambda makes shallow copies of objects
+        t.not(object.obj, actual.arr)
+        t.not(object.arr, actual_lodash.arr)
+        // t.not(object.arr, actual_rambda.arr) // this fails because Rambda makes shallow copies of arrays
+    })
+
+    // test.skip(
+    //     name +
+    //         ': should not overwrite existing values with `undefined` values of object sources',
+    //     function(t) {
+    //         var actual = merge({ a: 1 }, { a: undefined, b: undefined })
+    //         var expected = { a: 1, b: undefined }
+    //         t.deepEqual(actual, expected)
+    //     }
+    // )
+
+    // test.skip(
+    //     name +
+    //         ': should not overwrite existing values with `undefined` values of array sources',
+    //     function(t) {
+    //         var array = [1]
+    //         array[2] = 3
+
+    //         var actual = merge([4, 5, 6], array),
+    //             expected = [1, 5, 3]
+
+    //         t.deepEqual(actual, expected)
+
+    //         array = [1, , 3]
+    //         array[1] = undefined
+
+    //         actual = merge([4, 5, 6], array)
+    //         t.deepEqual(actual, expected)
+    //     }
+    // )
+
+    // test.skip(
+    //     name +
+    //         ': should convert values to arrays when merging arrays of `source`',
+    //     function(t) {
+    //         var object = { a: { '1': 'y', b: 'z', length: 2 } }
+    //         var actual = merge(object, { a: ['x'] })
+    //         var expected = { a: ['x', 'y'] }
+
+    //         t.deepEqual(actual, expected)
+
+    //         actual = merge({ a: {} }, { a: [] })
+    //         t.deepEqual(actual, { a: [] })
+    //     }
+    // )
+
+    // test.skip(
+    //     name + ': should merge sources containing circular references',
+    //     function(t) {
+    //         var object = {
+    //             foo: { a: 1 },
+    //             bar: { a: 2 }
+    //         }
+
+    //         var source = {
+    //             foo: { b: { c: { d: {} } } },
+    //             bar: {}
+    //         }
+
+    //         source.foo.b.c.d = source
+    //         source.bar.b = source.foo.b
+
+    //         var actual = merge(object, source)
+
+    //         t.notDeepEqual(actual.bar.b, actual.foo.b)
+    //         t.deepEqual(actual.foo.b.c.d, actual.foo.b.c.d.foo.b.c.d)
+    //     }
+    // )
+
+    // test.skip(name + ': should merge `arguments` objects', function(t) {
+    //     var object1 = { value: args },
+    //         object2 = { value: { '3': 4 } },
+    //         expected = { '0': 1, '1': 2, '2': 3, '3': 4 },
+    //         actual = merge(object1, object2)
+
+    //     t.true(!('3' in args))
+    //     t.true(!isArguments(actual.value))
+    //     t.deepEqual(actual.value, expected)
+    //     object1.value = args
+
+    //     actual = merge(object2, object1)
+    //     t.true(!isArguments(actual.value))
+    //     t.deepEqual(actual.value, expected)
+
+    //     expected = { '0': 1, '1': 2, '2': 3 }
+
+    //     actual = merge({}, object1)
+    //     t.true(!isArguments(actual.value))
+    //     t.deepEqual(actual.value, expected)
+    // })
+
+    // test.skip(name + ': should deep clone array/typed-array/plain-object source values', function(t) {
+    //     var typedArray = Uint8Array ? new Uint8Array([1]) : { buffer: [1] }
+
+    //     var props = ['0', 'buffer', 'a'],
+    //         values = [[{ a: 1 }], typedArray, { a: [1] }],
+    //         expected = _.map(values, () => true)
+
+    //     var actual = _.map(values, function(value, index) {
+    //         var key = props[index],
+    //             object = merge({}, { value: value }),
+    //             subValue = value[key],
+    //             newValue = object.value,
+    //             newSubValue = newValue[key]
+
+    //         return (
+    //             newValue !== value &&
+    //             newSubValue !== subValue &&
+    //             _.isEqual(newValue, value)
+    //         )
+    //     })
+
+    //     t.deepEqual(actual, expected)
+    // })
+
+    // test.skip(name + ': should merge typed arrays', function(t) {
+    //     var typedArrays = [
+    //         'Float32Array',
+    //         'Float64Array',
+    //         'Int8Array',
+    //         'Int16Array',
+    //         'Int32Array',
+    //         'Uint8Array',
+    //         'Uint8ClampedArray',
+    //         'Uint16Array',
+    //         'Uint32Array'
+    //     ]
+
+    //     var array1 = [0],
+    //         array2 = [0, 0],
+    //         array3 = [0, 0, 0, 0],
+    //         array4 = [0, 0, 0, 0, 0, 0, 0, 0]
+
+    //     var arrays = [
+    //             array2,
+    //             array1,
+    //             array4,
+    //             array3,
+    //             array2,
+    //             array4,
+    //             array4,
+    //             array3,
+    //             array2
+    //         ],
+    //         buffer = ArrayBuffer && new ArrayBuffer(8)
+
+    //     var expected = _.map(typedArrays, function(type, index) {
+    //         var array = arrays[index].slice()
+    //         array[0] = 1
+    //         return root[type] ? { value: array } : false
+    //     })
+
+    //     var actual = _.map(typedArrays, function(type) {
+    //         var Ctor = root[type]
+    //         return Ctor ? merge({ value: new Ctor(buffer) }, { value: [1] }) : false
+    //     })
+
+    //     t.true(_.isArray(actual))
+    //     t.deepEqual(actual, expected)
+
+    //     expected = _.map(typedArrays, function(type, index) {
+    //         var array = arrays[index].slice()
+    //         array.push(1)
+    //         return root[type] ? { value: array } : false
+    //     })
+
+    //     actual = _.map(typedArrays, function(type, index) {
+    //         var Ctor = root[type],
+    //             array = _.range(arrays[index].length)
+
+    //         array.push(1)
+    //         return Ctor
+    //             ? merge({ value: array }, { value: new Ctor(buffer) })
+    //             : false
+    //     })
+
+    //     t.true(_.isArray(actual))
+    //     t.deepEqual(actual, expected)
+    // })
+
+    // test.skip(name + ': should not error on DOM elements', function(t) {
+    //     var object1 = { el: document && document.createElement('div') },
+    //         object2 = { el: document && document.createElement('div') },
+    //         pairs = [[{}, object1], [object1, object2]],
+    //         expected = _.map(pairs, () => true)
+
+    //     var actual = _.map(pairs, function(pair) {
+    //         try {
+    //             return merge(pair[0], pair[1]).el === pair[1].el
+    //         } catch (e) {}
+    //     })
+
+    //     t.deepEqual(actual, expected)
+    // })
+}
diff --git a/test/mutation.js b/test/mutation.js
deleted file mode 100644
index 71528c0..0000000
--- a/test/mutation.js
+++ /dev/null
@@ -1,393 +0,0 @@
-var test = require('tape')
-// require('tabe').createStream( test );
-var dop = require('./.proxy').create()
-var set = dop.set
-var del = dop.del
-
-// THIS MUST BE CHANGED TO USE THE CURRENT RATHER THAN THIS HACK FOR THE OLD ONE
-// THIS MUST BE CHANGED TO USE THE CURRENT RATHER THAN THIS HACK FOR THE OLD ONE
-// THIS MUST BE CHANGED TO USE THE CURRENT RATHER THAN THIS HACK FOR THE OLD ONE
-dop.observe = function(object, property) {
-    var args = arguments
-    callback = args[args.length - 1]
-
-    var observer = dop.createObserver(callback)
-
-    args.length === 2
-        ? observer.observeObject(object)
-        : observer.observeProperty(object, property)
-
-    return observer
-}
-// THIS MUST BE CHANGED TO USE THE CURRENT RATHER THAN THIS HACK FOR THE OLD ONE
-// THIS MUST BE CHANGED TO USE THE CURRENT RATHER THAN THIS HACK FOR THE OLD ONE
-// THIS MUST BE CHANGED TO USE THE CURRENT RATHER THAN THIS HACK FOR THE OLD ONE
-
-var object = dop.register({ deep1: { deep2: {} } })
-var array = dop.register({ array: [1, 2, 3] })
-
-test('Object.set', function(t) {
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, object, '.object')
-        t.equal(mutation.prop, 'newprop', '.prop')
-        t.equal(mutation.value, '12345', '.value')
-        t.equal(mutation.old_value, undefined, '.old_value')
-        t.deepEqual(mutation.path, [1], '.path')
-    }
-    var observer = dop.observe(object, fun)
-    set(object, 'newprop', '12345')
-    observer.destroy()
-
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, object.deep1, '.object')
-        t.equal(mutation.prop, 'newprop', '.prop')
-        t.equal(mutation.value, '12345', '.value')
-        t.equal(mutation.old_value, undefined, '.old_value')
-        t.deepEqual(mutation.path, [1, 'deep1'], '.path')
-    }
-    var observer = dop.observe(object.deep1, fun)
-    set(object.deep1, 'newprop', '12345')
-    observer.destroy()
-
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, object.deep1.deep2, '.object')
-        t.equal(mutation.prop, 'newprop', '.prop')
-        t.equal(mutation.value, '12345', '.value')
-        t.equal(mutation.old_value, undefined, '.old_value')
-        t.deepEqual(mutation.path, [1, 'deep1', 'deep2'], '.path')
-    }
-    var observer = dop.observe(object.deep1.deep2, fun)
-    set(object.deep1.deep2, 'newprop', '12345')
-    observer.destroy()
-
-    t.end()
-})
-
-test('Object.del', function(t) {
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, object, '.object')
-        t.equal(mutation.prop, 'newprop', '.prop')
-        t.equal(mutation.value, undefined, '.value')
-        t.equal(mutation.old_value, '12345', '.old_value')
-        t.deepEqual(mutation.path, [1], '.path')
-    }
-    var observer = dop.observe(object, fun)
-    del(object, 'newprop')
-    observer.destroy()
-
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, object.deep1, '.object')
-        t.equal(mutation.old_value, '12345', '.old_value')
-        t.equal(mutation.prop, 'newprop', '.prop')
-        t.equal(mutation.value, undefined, '.value')
-        t.deepEqual(mutation.path, [1, 'deep1'], '.path')
-    }
-    var observer = dop.observe(object.deep1, fun)
-    del(object.deep1, 'newprop')
-    observer.destroy()
-
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, object.deep1.deep2, '.object')
-        t.equal(mutation.prop, 'newprop', '.prop')
-        t.equal(mutation.value, undefined, '.value')
-        t.equal(mutation.old_value, '12345', '.old_value')
-        t.deepEqual(mutation.path, [1, 'deep1', 'deep2'], '.path')
-    }
-    var observer = dop.observe(object.deep1.deep2, fun)
-    del(object.deep1.deep2, 'newprop')
-    observer.destroy()
-
-    t.end()
-})
-
-test('Object.del change value', function(t) {
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, object.deep1, '.object')
-        t.equal(mutation.prop, 'newprop', '.prop')
-        t.equal(mutation.value, '67890', '.value')
-        t.equal(mutation.old_value, undefined, '.old_value')
-        t.deepEqual(mutation.path, [1, 'deep1'], '.path')
-    }
-    var observer = dop.observe(object.deep1, fun)
-    set(object.deep1, 'newprop', '67890')
-    observer.destroy()
-
-    t.end()
-})
-
-test('Object.del subobject', function(t) {
-    var deep2 = object.deep1.deep2
-
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, object.deep1, '.object')
-        t.equal(mutation.prop, 'deep2', '.prop')
-        t.deepEqual(mutation.value, {}, '.value')
-        t.deepEqual(mutation.old_value, dop.util.clone(deep2), '.old_value')
-        t.deepEqual(mutation.path, [1, 'deep1'], '.path')
-    }
-    var observer = dop.observe(object.deep1, fun)
-    set(object.deep1, 'deep2', {})
-    observer.destroy()
-
-    try {
-        var observer = dop.observe(deep2, function() {})
-    } catch (e) {
-        t.equal(
-            e instanceof Error,
-            true,
-            'This must throw an error because deep2 is not inside of a registered object anymore'
-        )
-    }
-
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, object.deep1.deep2, '.object')
-        t.equal(mutation.prop, 'deep3', '.prop')
-        t.deepEqual(mutation.value, 'hola mundo', '.value')
-        t.deepEqual(mutation.old_value, undefined, '.old_value')
-        t.deepEqual(mutation.path, [1, 'deep1', 'deep2'], '.path')
-    }
-    var observer = dop.observe(object.deep1.deep2, fun)
-    set(object.deep1.deep2, 'deep3', 'hola mundo')
-    observer.destroy()
-
-    t.end()
-})
-
-test('Array.set', function(t) {
-    var first = true
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        if (first) {
-            t.equal(mutation.object, array.array, '.object')
-            t.equal(mutation.prop, '5', '.prop')
-            t.deepEqual(mutation.value, [true], '.value')
-            t.equal(mutation.old_value, undefined, '.old_value')
-            t.deepEqual(mutation.path, [2, 'array'], '.path')
-            first = false
-        } else {
-            t.equal(mutation.object, array.array, '.object')
-            t.equal(mutation.prop, 'length', '.prop')
-            t.deepEqual(mutation.value, 6, '.value')
-            t.equal(mutation.old_value, 3, '.old_value')
-            t.deepEqual(mutation.path, [2, 'array'], '.path')
-        }
-    }
-    var observer = dop.observe(array.array, fun)
-    var observer2 = dop.observe(array.array, 'length', function(mutations) {
-        t.equal(mutations.length, 1, 'mutations length')
-    })
-    set(array.array, 5, [true])
-    observer.destroy()
-    observer2.destroy()
-
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, array.array[5], '.object')
-        t.equal(mutation.prop, '0', '.prop')
-        t.equal(mutation.value, false, '.value')
-        t.equal(mutation.old_value, true, '.old_value')
-        t.deepEqual(mutation.path, [2, 'array', 5], '.path')
-    }
-    var observer = dop.observe(array.array[5], fun)
-    set(array.array[5], 0, false)
-    observer.destroy()
-
-    t.end()
-})
-
-test('Array.del', function(t) {
-    var a = array.array[5]
-    t.deepEqual(dop.getObjectPath(a), [2, 'array', 5])
-
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, array.array, '.object')
-        t.equal(mutation.prop, '5', '.prop')
-        t.equal(mutation.value, undefined, '.value')
-        t.deepEqual(mutation.old_value, [false], '.old_value')
-        t.deepEqual(mutation.path, [2, 'array'], '.path')
-    }
-    var observer = dop.observe(array.array, fun)
-    del(array.array, 5)
-    observer.destroy()
-    t.equal(dop.getObjectPath(a), undefined)
-
-    t.end()
-})
-
-test('Array.splice', function(t) {
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        var arr = []
-        arr.length = 3
-        t.equal(mutation.object, array.array, '.object')
-        t.equal(mutation.prop, 'array', '.prop')
-        t.deepEqual(mutation.splice, [3, 3, [true]], '.splice')
-        t.deepEqual(mutation.spliced, arr, '.spliced')
-        t.equal(mutation.spliced.length, arr.length, '.spliced length')
-        t.deepEqual(mutation.path, [2, 'array'], '.path')
-    }
-    var observer = dop.observe(array.array, fun)
-    var observer2 = dop.observe(array.array, 'length', function(mutations) {
-        t.equal(mutations.length, 1, 'mutations length')
-        var mutation = mutations[0]
-        t.deepEqual(array.array.length, 4, 'array.array.length')
-        t.deepEqual(mutation.object.length, 4, '.object.length')
-        t.deepEqual(mutation.old_length, 6, '.old_length')
-        t.equal(mutation.object, array.array, '.object')
-        t.equal(mutation.prop, 'array', '.prop')
-        t.deepEqual(mutation.splice, [3, 3, [true]], '.splice')
-        t.deepEqual(mutation.path, [2, 'array'], '.path')
-    })
-    array.array.splice(3, 3, [true])
-    observer.destroy()
-    observer2.destroy()
-
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, array.array, '.object')
-        t.equal(mutation.prop, 'array', '.prop')
-        t.deepEqual(mutation.splice, [3, 1, [false]], '.splice')
-        t.deepEqual(mutation.spliced, [[true]], '.spliced')
-        t.deepEqual(mutation.path, [2, 'array'], '.path')
-    }
-    var observer = dop.observe(array.array, fun)
-    array.array.splice(3, 1, [false])
-    observer.destroy()
-
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, array.array, '.object')
-        t.equal(mutation.prop, 'array', '.prop')
-        t.deepEqual(mutation.splice, [1, 0, { deep: true }], '.splice')
-        t.deepEqual(mutation.spliced, undefined, '.spliced')
-        t.deepEqual(mutation.path, [2, 'array'], '.path')
-    }
-    var observer = dop.observe(array.array, fun)
-    array.array.splice(1, 0, { deep: true })
-    observer.destroy()
-
-    t.end()
-})
-
-test('Array.reverse', function(t) {
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, array.array, '.object')
-        t.equal(mutation.prop, 'array', '.prop')
-        t.deepEqual(mutation.swaps, [0, 4, 1, 3], '.swaps')
-        t.deepEqual(mutation.path, [2, 'array'], '.path')
-    }
-
-    var objDeep = array.array[1]
-    t.deepEqual(dop.getObjectPath(objDeep), [2, 'array', 1], 'getObjectPath')
-    var observer = dop.observe(array.array, fun)
-    array.array.reverse()
-    observer.destroy()
-    t.equal(objDeep, array.array[3])
-    t.deepEqual(dop.getObjectPath(objDeep), [2, 'array', 3], 'getObjectPath')
-
-    t.end()
-})
-
-test('Array.sort', function(t) {
-    fun = function(mutations) {
-        var mutation = mutations[0]
-        t.equal(mutation.object, array.array, '.object')
-        t.equal(mutation.prop, 'array', '.prop')
-        t.deepEqual(mutation.swaps, [0, 4, 1, 2], '.swaps')
-        t.deepEqual(mutation.path, [2, 'array'], '.path')
-    }
-
-    var objDeep = array.array[0]
-    t.deepEqual(dop.getObjectPath(objDeep), [2, 'array', 0], 'getObjectPath')
-    var observer = dop.observe(array.array, fun)
-    array.array.sort()
-    observer.destroy()
-    t.equal(objDeep, array.array[4])
-    t.deepEqual(dop.getObjectPath(objDeep), [2, 'array', 4], 'getObjectPath')
-
-    t.end()
-})
-
-test('Same items after array mutations', function(t) {
-    var array = dop.register({ array: [{ a: 1 }, { b: 2 }, { c: 3 }] })
-    var obj = dop.register({ e: 5 })
-    var tmp = array.array[0]
-    array.array.unshift({ d: 4 })
-    t.equal(tmp, array.array[1], 'same object after unshift')
-
-    array.array.reverse()
-    t.equal(tmp, array.array[2], 'same object after reverse')
-
-    array.array.unshift(obj)
-    t.deepEqual(obj, array.array[0], 'should be the same')
-    t.notEqual(obj, array.array[0], 'should be different')
-
-    t.deepEqual(dop.getObjectPath(obj), [4], 'correct path')
-    t.deepEqual(
-        dop.getObjectPath(array.array[0]),
-        [3, 'array', 0],
-        'correct path'
-    )
-
-    t.end()
-})
-
-test('Setting objects already registered', function(t) {
-    var a = dop.register({ a: 1 })
-    var b = dop.register({ b: 2 })
-
-    set(a, 'b', b)
-
-    t.deepEqual(a.b, b, 'should be the same')
-    t.notEqual(a.b, b, 'should be different')
-    t.deepEqual(dop.getObjectPath(a.b), [5, 'b'], 'correct path')
-    t.deepEqual(dop.getObjectPath(b), [6], 'correct path')
-
-    t.end()
-})
-
-test('unobserve = observer.observe(...)', function(t) {
-    var object = dop.register({})
-    var times = 0
-    var observer = dop.createObserver(function(mutations) {
-        times += 1
-    })
-    var tounobserve = observer.observeObject(object)
-    set(object, 'newprop', '12345')
-    t.equal(times, 1)
-    set(object, 'newprop', '12345')
-    t.equal(times, 1)
-    set(object, 'newprop', '555')
-    t.equal(times, 2)
-    tounobserve()
-    set(object, 'newprop', 'blabla')
-    t.equal(times, 2)
-
-    t.end()
-})
-
-test('mutating length of array', function(t) {
-    var object = dop.register({ array: [1, 2, 3] })
-    var times = 0
-    var observer = dop.createObserver(function(mutations) {
-        times += 1
-    })
-    observer.observeObject(object.array)
-    observer.observeProperty(object.array, 'length')
-    set(object.array, 'length', 1)
-    t.equal(object.array.length, 1)
-    t.equal(times, 1)
-
-    t.end()
-})
diff --git a/test/observeAll.js b/test/observeAll.js
deleted file mode 100644
index ad3d447..0000000
--- a/test/observeAll.js
+++ /dev/null
@@ -1,63 +0,0 @@
-var test = require('tape')
-// require('tabe').createStream( test );
-var dop = require('./.proxy').create()
-
-test('observing root', function(t) {
-    var object = dop.register({
-        a: { a1: true },
-        b: { b1: { b11: true } }
-    })
-    var observer = dop.createObserver(function(mutations) {
-        t.equal(mutations.length, 1)
-        t.end()
-    })
-    // observer.observeAll(object.b)
-    observer.observeAll(object)
-    dop.set(object.b.b1, 'b11', false)
-})
-
-test('mutation above', function(t) {
-    var object = dop.register({
-        a: true,
-        b: { b1: { b11: true } }
-    })
-    var observer = dop.createObserver(function(mutations) {
-        t.equal(true, false, 'This should not happen')
-    })
-    observer.observeAll(object.b)
-    dop.set(object, 'a', false)
-    t.equal(true, true, 'All good')
-    t.end()
-})
-
-test('mutating both levels observing root', function(t) {
-    var object = dop.register({
-        a: true,
-        b: { b1: { b11: true } }
-    })
-    var observer = dop.createObserver(function(mutations) {
-        t.equal(mutations.length, 2)
-        t.end()
-    })
-    observer.observeAll(object)
-    dop.action(function() {
-        dop.set(object, 'a', false)
-        dop.set(object.b.b1, 'b11', false)
-    })()
-})
-
-test('mutating both levels observing deep', function(t) {
-    var object = dop.register({
-        a: true,
-        b: { b1: { b11: true } }
-    })
-    var observer = dop.createObserver(function(mutations) {
-        t.equal(mutations.length, 1)
-        t.end()
-    })
-    observer.observeAll(object.b.b1)
-    dop.action(function() {
-        dop.set(object, 'a', false)
-        dop.set(object.b.b1, 'b11', false)
-    })()
-})
diff --git a/test/observers.js b/test/observers.js
deleted file mode 100644
index 6f6a614..0000000
--- a/test/observers.js
+++ /dev/null
@@ -1,75 +0,0 @@
-var test = require('tape')
-// require('tabe').createStream( test );
-var dop = require('./.proxy').create()
-
-var object = dop.register({ uno: 'A', dos: 'B' })
-var object2 = dop.register({ uno: 1, cuatro: 4 })
-// dop.createObserver(object, myfun)
-// object.uno = 25
-
-test('createObserver.observe', function(t) {
-    function first(mutations) {
-        // console.log( mutations );
-    }
-    function second() {}
-
-    var observer = dop.createObserver(first)
-    var observer2 = dop.createObserver(first)
-    var observer3 = dop.createObserver(second)
-    t.equal(dop.data.observers[observer.id], observer2, 'Must be same object')
-    t.equal(observer, observer2, 'observe and observe2 must be same observers')
-    t.notEqual(
-        observer,
-        observer3,
-        'observe and observe3 must be note same observers'
-    )
-    t.equal(Object.keys(dop.data.observers).length, 2, 'Only two observers ')
-
-    observer.destroy()
-    observer3.destroy()
-
-    t.end()
-})
-
-test('mutations with createObserver', function(t) {
-    var muta
-    var observer = dop.createObserver(function first(mutations) {
-        t.equal(
-            mutations.length,
-            2,
-            'Two mutations received for the first observer'
-        )
-        muta = mutations
-    })
-    var observer2 = dop.createObserver(function second(mutations) {
-        t.equal(
-            mutations.length,
-            1,
-            'One mutations received for the second observer'
-        )
-        t.equal(
-            mutations[0],
-            muta[0],
-            'The first mutation is the same object than the first observer'
-        )
-        t.end()
-    })
-    observer.observeObject(object)
-    observer.observeProperty(object, 'uno')
-    observer2.observeProperty(object, 'uno')
-
-    collector = dop.collect()
-    dop.set(object, 'uno', 123)
-    dop.set(object, 'new', 123)
-    collector.emit()
-})
-
-test('check swap mutation with createObserver', function(t) {
-    var object = dop.register({ array: [1, 2, 3] })
-    var observer = dop.createObserver(function first(mutations) {
-        t.equal(Array.isArray(mutations[0].swaps), true)
-        t.end()
-    })
-    observer.observeObject(object.array)
-    object.array.reverse()
-})
diff --git a/test/produce_patch.js b/test/produce_patch.js
new file mode 100644
index 0000000..5c4e21d
--- /dev/null
+++ b/test/produce_patch.js
@@ -0,0 +1,96 @@
+import test from 'ava'
+import { applyPatch, merge } from '../src'
+import { producePatch } from '../src/util/patches'
+import { isPlainObject, isArray } from '../src/util/is'
+import { testPatchUnpatch } from './utils'
+
+test('types: Draft is a completly new copy with proxies', function (t) {
+    const object = {
+        array: [{}, []],
+        object: { o: new Object(), a: new Array() },
+    }
+
+    producePatch(object, (draft) => {
+        t.deepEqual(object, draft)
+        t.not(object, draft)
+        t.not(object.array, draft.array)
+        t.not(object.array[0], draft.array[0])
+        t.not(object.array[1], draft.array[1])
+        t.not(object.object, draft.object)
+        t.not(object.object.o, draft.object.o)
+        t.not(object.object.a, draft.object.a)
+    })
+})
+
+test('types: Array shoult not be the same', function (t) {
+    const array = []
+    producePatch(array, (draft) => {
+        t.not(array, draft)
+    })
+})
+
+test('types: Not an object or array', function (t) {
+    producePatch(12345, (draft) => {
+        t.is(12345, draft)
+    })
+})
+
+test('types: If is not plain object or array we ignore it', function (t) {
+    const date = new Date()
+    producePatch(date, (draft) => {
+        t.is(date, draft)
+    })
+})
+
+test('api: patch and mutations is the output', function (t) {
+    const date = new Date()
+    const { patch, mutations } = producePatch(date, () => {})
+    t.true(isPlainObject(patch))
+    t.true(isArray(mutations))
+})
+
+test('mutations: new object', function (t) {
+    const target = {}
+    const expected = { a: { bb: {} } }
+    const { patch } = producePatch(target, (d) => {
+        d.a = { bb: {} }
+    })
+
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('mutations: *', function (t) {
+    let copy_draft
+    const target = {
+        change: false,
+        array: [1, new Date(), 3],
+        obj: { deepchange: false, delete: 0 },
+    }
+    const newobject = { hello: 'world' }
+
+    const { patch, mutations } = producePatch(target, (draft) => {
+        t.deepEqual(target, draft)
+
+        // mutations
+        delete draft.obj.delete
+        draft.change = true
+        draft.array[3] = 4
+        const arr = []
+        draft.array.shift()
+        draft.array.reverse()
+        draft.array.unshift('first')
+        arr.push(1234)
+        draft.obj.deepchange = true
+        draft.obj.new = 'string'
+        draft.obj.newobject = newobject
+
+        copy_draft = merge({}, draft)
+        t.deepEqual(copy_draft, draft)
+    })
+
+    // console.log(mutations)
+    // console.log(patch)
+    applyPatch(target, patch)
+    t.is(target.obj.newobject, newobject)
+    t.deepEqual(target, copy_draft)
+})
diff --git a/test/protocol/.connect.js b/test/protocol/.connect.js
deleted file mode 100644
index 1a35b8d..0000000
--- a/test/protocol/.connect.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const dop = require('../.proxy')
-const transportName = process.argv[2] || 'local'
-const transportListen = require('dop-transports').listen[transportName]
-const transportConnect = require('dop-transports').connect[transportName]
-
-function connect(t, end = true, port1 = 12345, port2 = 23456) {
-    return new Promise(function(response, reject) {
-        const dopServer = dop.create()
-        const dopClient = dop.create()
-        const dopSubClient = dop.create()
-        dopServer.env = 'SERVER'
-        dopClient.env = 'CLIENT'
-        dopSubClient.env = 'SUBCLIENT'
-        const transportServer = dopServer.listen({
-            transport: transportListen,
-            port: port1
-        })
-        const transportClient = dopClient.connect({
-            transport: transportConnect,
-            url: 'ws://localhost:' + port1
-        })
-
-        transportClient.on('connect', function(nodeClient) {
-            const transportClientListen = dopClient.listen({
-                transport: transportListen,
-                port: port2
-            })
-            const transportSubClient = dopSubClient.connect({
-                transport: transportConnect,
-                url: 'ws://localhost:' + port2
-            })
-            transportSubClient.on('connect', function(nodeSubClient) {
-                const close = function() {
-                    nodeSubClient.disconnect()
-                    transportServer.socket.close()
-                    nodeClient.disconnect()
-                    transportClientListen.socket.close()
-                    if (end) t.end()
-                }
-                response({
-                    dopServer,
-                    dopClient,
-                    dopSubClient,
-                    transportServer,
-                    transportClient,
-                    transportSubClient,
-                    nodeClient,
-                    nodeSubClient,
-                    close
-                })
-            })
-        })
-    })
-}
-
-module.exports = { connect }
diff --git a/test/protocol/call.js b/test/protocol/call.js
deleted file mode 100644
index 95fd889..0000000
--- a/test/protocol/call.js
+++ /dev/null
@@ -1,4 +0,0 @@
-try {
-    eval('async () => {}')
-    require('../ES6/call')
-} catch (e) {}
diff --git a/test/protocol/subscribe.js b/test/protocol/subscribe.js
deleted file mode 100644
index 3839b2c..0000000
--- a/test/protocol/subscribe.js
+++ /dev/null
@@ -1,4 +0,0 @@
-try {
-    eval('async () => {}')
-    require('../ES6/subscribe')
-} catch (e) {}
diff --git a/test/register.js b/test/register.js
deleted file mode 100644
index 4380ca6..0000000
--- a/test/register.js
+++ /dev/null
@@ -1,123 +0,0 @@
-var test = require('tape')
-// require('tabe').createStream( test );
-var dop = require('./.proxy').create()
-var objectTarget = { deep1: { deep2: {} } }
-var object = dop.register(objectTarget)
-
-test('Object root', function(t) {
-    t.equal(dop.getObjectRoot(object), dop.getObjectProxy(object))
-    t.equal(dop.getObjectRoot(object.deep1), dop.getObjectProxy(object))
-    t.equal(dop.getObjectRoot(object.deep1.deep2), dop.getObjectProxy(object))
-    t.end()
-})
-
-test('Object parent', function(t) {
-    t.equal(dop.getObjectParent(object), undefined)
-    t.equal(dop.getObjectParent(object.deep1), dop.getObjectProxy(object))
-    t.equal(
-        dop.getObjectParent(object.deep1.deep2),
-        dop.getObjectProxy(object.deep1)
-    )
-    t.end()
-})
-
-test('Object property', function(t) {
-    t.equal(dop.getObjectProperty(object), 1)
-    t.equal(dop.getObjectProperty(object.deep1), 'deep1')
-    t.equal(dop.getObjectProperty(object.deep1.deep2), 'deep2')
-    t.end()
-})
-
-test('Object proxy and target', function(t) {
-    t.equal(dop.getObjectProxy(object), object)
-    t.equal(dop.getObjectProxy(objectTarget), object)
-    t.equal(dop.getObjectTarget(object), objectTarget)
-    t.equal(dop.getObjectTarget(objectTarget), objectTarget)
-    t.end()
-})
-
-test('Correct configuration on deleted neested object', function(t) {
-    var object = dop.register({
-        subobject: {
-            name: 'Josema',
-            surname: 'Gonzalez'
-        }
-    })
-    var copy = object.subobject
-    dop.del(object, 'subobject')
-    dop.set(copy, 'lol', { test: 1234 })
-    t.equal(dop.getObjectRoot(copy.lol), object)
-    t.equal(dop.getObjectPath(copy.lol), undefined)
-    t.deepEqual(dop.getObjectPath(copy.lol, false), [2, 'subobject', 'lol'])
-    dop.set(object, 'subobject', copy)
-    t.deepEqual(dop.getObjectPath(copy.lol), [2, 'subobject', 'lol'])
-
-    t.end()
-})
-
-test('register({})', function(t) {
-    var o = dop.register({})
-    t.equal(typeof o, 'object')
-    t.end()
-})
-
-test('register(new Date)', function(t) {
-    var o = dop.register(new Date())
-    t.equal(typeof o, 'object')
-    t.end()
-})
-
-test('register([])', function(t) {
-    try {
-        var o = dop.register([])
-    } catch (e) {
-        t.equal(typeof e, 'object')
-        t.end()
-    }
-})
-
-// var object = dop.register({
-//     prop: "prop",
-//     subobject:{ value:1, valua:2, subsubobject:{val:3}},
-//     prearr: {arr: [{a:1},{b:2},{c:3,subarr:[{d:4}]}]}
-// });
-
-// test('Level', function(t) {
-//     t.equal(object["~DOP"].l, 1);
-//     t.equal(object.subobject["~DOP"].l, 2);
-//     t.equal(object.subobject.subsubobject["~DOP"].l,3);
-//     t.equal(object.prearr.arr[2].subarr[0]["~DOP"].l, 6);
-//     t.end();
-// })
-// test('Level after editing', function(t) {
-//     object.prop = {testing:{editing:{end:9999,arr:[{c:1}]}}}
-//     t.equal(object.prop["~DOP"].l, 2);
-//     t.equal(object.prop.testing.editing["~DOP"].l, 4);
-//     t.equal(object.prop.testing.editing.arr[0]["~DOP"].l, 6);
-//     t.equal(object["~DOP"].l, 1);
-//     t.equal(object.subobject["~DOP"].l, 2);
-//     t.equal(object.subobject.subsubobject["~DOP"].l,3);
-//     t.equal(object.prearr.arr[2].subarr[0]["~DOP"].l, 6);
-//     t.end();
-// })
-
-// test('Is inside of array', function(t) {
-//     t.equal(object.prop["~DOP"].ia, false);
-//     t.equal(object.prop.testing.editing["~DOP"].ia, false);
-//     t.equal(object.prop.testing.editing.arr[0]["~DOP"].ia, true);
-//     t.equal(object["~DOP"].ia, false);
-//     t.equal(object.subobject["~DOP"].ia, false);
-//     t.equal(object.prearr.arr[2].subarr[0]["~DOP"].ia, true);
-//     t.end();
-
-// dop.createObserver(object.subobject, function(){
-//     console.log('changed', object.subobject.value)
-// })
-
-// copy = object.subobject
-// object.subobject.value = 111
-// object.subobject = {value:'new'}
-// object.subobject.value = 222
-// copy.value = 222
-
-// })
diff --git a/test/rpcs.js b/test/rpcs.js
new file mode 100644
index 0000000..5b0efeb
--- /dev/null
+++ b/test/rpcs.js
@@ -0,0 +1,402 @@
+import test from 'ava'
+import { createNode } from '../src'
+
+const localFunctions = {
+    okSync: function (req) {
+        return 'Ok'
+    },
+    okAsync: async function (req) {
+        return 'Ok'
+    },
+    okPromise: function (req) {
+        return new Promise(function (resolve, reject) {
+            resolve('Ok')
+        })
+    },
+
+    undefinedSync: function (req) {},
+    undefinedAsync: async function (req) {},
+    undefinedPromise: function (req) {
+        return new Promise(function (resolve, reject) {
+            resolve(undefined)
+        })
+    },
+
+    errorSync: function (req) {
+        throw 'Error'
+    },
+    errorAsync: async function (req) {
+        throw 'Error'
+    },
+    errorPromise: function (req) {
+        return new Promise(function (resolve, reject) {
+            reject('Error')
+        })
+    },
+
+    timeoutSync: function (req) {
+        setTimeout(function () {
+            req.resolve('Ok')
+        }, 10)
+        return req
+    },
+    timeoutAsync: async function (req) {
+        setTimeout(function () {
+            req.resolve('Ok')
+        }, 10)
+        return req
+    },
+    timeoutPromise: async function (req) {
+        return new Promise(function (resolve, reject) {
+            setTimeout(function () {
+                req.resolve('Ok')
+            }, 10)
+            resolve(req)
+        })
+    },
+
+    reqresolveSync: function (req) {
+        return req.resolve('ok')
+    },
+    reqresolveAsync: async function (req) {
+        return req.resolve('ok')
+    },
+    reqresolvePromise: function (req) {
+        return new Promise(function (resolve, reject) {
+            resolve(req.resolve('ok'))
+        })
+    },
+
+    presolveSync: function (req) {
+        return Promise.resolve('ok')
+    },
+    presolveAsync: async function (req) {
+        return Promise.resolve('ok')
+    },
+    presolvePromise: function (req) {
+        return new Promise(function (resolve, reject) {
+            resolve(Promise.resolve('ok'))
+        })
+    },
+
+    reqrejectSync: function (req) {
+        return req.reject('Error')
+    },
+    reqrejectAsync: async function (req) {
+        return req.reject('Error')
+    },
+    reqrejectPromise: function (req) {
+        return new Promise(function (resolve, reject) {
+            reject(req.reject('Error'))
+        })
+    },
+
+    prejectSync: function (req) {
+        return Promise.reject('Error')
+    },
+    prejectAsync: async function (req) {
+        return Promise.reject('Error')
+    },
+    prejectPromise: function (req) {
+        return new Promise(function (resolve, reject) {
+            resolve(Promise.reject('Error'))
+        })
+    },
+
+    referenceError: function () {
+        functionThatDoesNotExists()
+    },
+
+    throwError: function () {
+        throw new Error('whatever')
+    },
+
+    throwErrorAsync: async function () {
+        throw new Error('async whatever')
+    },
+
+    throwErrorAsyncRemote: async function () {
+        throw new Error('async whatever')
+    },
+
+    throwUndefined: function () {
+        throw undefined
+    },
+
+    throwNull: function () {
+        throw null
+    },
+
+    throwZero: function () {
+        throw 0
+    },
+}
+
+const server = createNode()
+const client = createNode()
+let remoteFunctions
+client.open(server.message, (r) => {
+    remoteFunctions = r
+})
+const callClient = server.open(client.message)
+callClient(localFunctions)
+
+test('throw a real error from async/await local', async function (t) {
+    try {
+        await localFunctions.throwErrorAsync()
+        t.true(false)
+    } catch (e) {
+        t.true(e instanceof Error)
+    }
+})
+
+// Don't know how to test this because it should break the script
+test.skip('throw a real error from async/await remote', async function (t) {
+    try {
+        await remoteFunctions.throwErrorAsyncRemote()
+        t.true(true)
+    } catch (e) {
+        t.true(false)
+    }
+})
+
+test('Matching same values', async function (t) {
+    t.deepEqual(Object.keys(remoteFunctions), Object.keys(localFunctions))
+})
+
+test('Ok', async function (t) {
+    t.is(await remoteFunctions.okSync(), await remoteFunctions.okAsync())
+    t.is(await remoteFunctions.okSync(), await remoteFunctions.okPromise())
+    t.is(await remoteFunctions.okSync(), await localFunctions.okSync())
+    t.is(await remoteFunctions.okAsync(), await localFunctions.okAsync())
+    t.is(await remoteFunctions.okPromise(), await localFunctions.okPromise())
+})
+
+test('Error', async function (t) {
+    t.is(1, 1)
+    try {
+        await remoteFunctions.errorSync()
+        t.is(1, 2, 'this should not happen')
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+    try {
+        await remoteFunctions.errorAsync()
+        t.is(1, 2, 'this should not happen')
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+    try {
+        await remoteFunctions.errorPromise()
+        t.is(1, 2, 'this should not happen')
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+
+    try {
+        await localFunctions.errorSync()
+        t.is(1, 2, 'this should not happen')
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+    try {
+        await localFunctions.errorAsync()
+        t.is(1, 2, 'this should not happen')
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+    try {
+        await localFunctions.errorPromise()
+        t.is(1, 2, 'this should not happen')
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+})
+
+test('timeout', async function (t) {
+    t.is(
+        await remoteFunctions.timeoutSync(),
+        await remoteFunctions.timeoutAsync()
+    )
+    t.is(
+        await remoteFunctions.timeoutSync(),
+        await remoteFunctions.timeoutPromise()
+    )
+})
+
+test('req.resolve', async function (t) {
+    t.is(
+        await remoteFunctions.reqresolveSync(),
+        await remoteFunctions.reqresolveAsync()
+    )
+    t.is(
+        await remoteFunctions.reqresolveSync(),
+        await remoteFunctions.reqresolvePromise()
+    )
+})
+
+test('Promise.resolve', async function (t) {
+    t.is(
+        await remoteFunctions.reqresolveSync(),
+        await remoteFunctions.reqresolveAsync()
+    )
+    t.is(
+        await remoteFunctions.reqresolveSync(),
+        await remoteFunctions.reqresolvePromise()
+    )
+})
+
+test('req.reject', async function (t) {
+    try {
+        await remoteFunctions.reqrejectSync()
+        t.is(1, 2, 'this should not happen')
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+    try {
+        await remoteFunctions.reqrejectAsync()
+        t.is(1, 2, 'this should not happen')
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+    try {
+        await remoteFunctions.reqrejectPromise()
+        t.is(1, 2, 'this should not happen')
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+})
+
+test('Promise.reject', async function (t) {
+    try {
+        await remoteFunctions.prejectSync()
+        t.is(1, 2, 'this should not happen')
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+    try {
+        await remoteFunctions.prejectAsync()
+        t.is(1, 2, 'this should not happen')
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+    try {
+        await remoteFunctions.prejectPromise()
+        t.is(1, 2, 'this should not happen')
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+})
+
+test('Undefined', async function (t) {
+    t.is(
+        await remoteFunctions.undefinedSync(),
+        await remoteFunctions.undefinedAsync()
+    )
+    t.is(
+        await remoteFunctions.undefinedSync(),
+        await remoteFunctions.undefinedPromise()
+    )
+    t.is(
+        await remoteFunctions.undefinedSync(),
+        await localFunctions.undefinedSync()
+    )
+    t.is(
+        await remoteFunctions.undefinedAsync(),
+        await localFunctions.undefinedAsync()
+    )
+    t.is(
+        await remoteFunctions.undefinedPromise(),
+        await localFunctions.undefinedPromise()
+    )
+})
+
+test('throw "Error"', async function (t) {
+    try {
+        remoteFunctions.errorSync().catch(() => {})
+        t.true(true)
+    } catch (e) {
+        t.true(false)
+    }
+    try {
+        await remoteFunctions.errorSync()
+        t.true(false)
+    } catch (e) {
+        t.is(e, 'Error')
+    }
+})
+
+test('ReferenceError error', async function (t) {
+    try {
+        remoteFunctions.referenceError()
+        t.true(false)
+    } catch (e) {
+        t.true(e instanceof ReferenceError)
+    }
+    try {
+        await remoteFunctions.referenceError()
+        t.true(false)
+    } catch (e) {
+        t.true(e instanceof ReferenceError)
+    }
+})
+
+test('throw new Error()', async function (t) {
+    try {
+        localFunctions.throwError()
+        t.true(false)
+    } catch (e) {
+        t.true(e instanceof Error)
+    }
+    try {
+        await remoteFunctions.throwError()
+        t.true(false)
+    } catch (e) {
+        t.true(e instanceof Error)
+    }
+})
+
+test('throwUndefined', async function (t) {
+    try {
+        localFunctions.throwUndefined()
+        t.true(false)
+    } catch (e) {
+        t.is(e, undefined)
+    }
+    try {
+        await remoteFunctions.throwUndefined()
+        t.true(false)
+    } catch (e) {
+        t.is(e, null)
+    }
+})
+
+test('throwNull', async function (t) {
+    try {
+        localFunctions.throwNull()
+        t.true(false)
+    } catch (e) {
+        t.is(e, null)
+    }
+    try {
+        await remoteFunctions.throwNull()
+        t.true(false)
+    } catch (e) {
+        t.is(e, null)
+    }
+})
+
+test('throwZero', async function (t) {
+    try {
+        localFunctions.throwZero()
+        t.true(false)
+    } catch (e) {
+        t.is(e, 0)
+    }
+    try {
+        await remoteFunctions.throwZero('lel')
+        t.true(false)
+    } catch (e) {
+        t.is(e, null)
+    }
+})
diff --git a/test/setpatchs.js b/test/setpatchs.js
deleted file mode 100644
index 9ddbf9c..0000000
--- a/test/setpatchs.js
+++ /dev/null
@@ -1,157 +0,0 @@
-var test = require('tape')
-var dop = require('./.proxy').create()
-var set = dop.set
-var del = dop.del
-
-function getPatch(collector) {
-    var patch = dop.core.getPatch(collector.mutations)
-    var patchServer = dop.decode(dop.encode(patch))
-    collector.destroy()
-    for (var id in patchServer) return patchServer[id].chunks
-}
-function setPatch(destiny, chunks) {
-    dop.core.setPatch(destiny, chunks, dop.core.setPatchMutator)
-}
-function makeTest(t, collector, objServer, objClient) {
-    // console.log( JSON.stringify(objServer) );
-    // console.log( JSON.stringify(objClient) );
-    var patch = getPatch(collector)
-    setPatch(objClient, patch)
-    // console.log( JSON.stringify(objServer) );
-    // console.log( JSON.stringify(objClient) );
-}
-
-var objServer = dop.register({
-    number: 1,
-    subobject: { value1: true },
-    array: [{ value1: true }, 'B', 'C', 4],
-    subobjectarray: { array: [{ value1: true }] }
-})
-var objClient = dop.register(dop.util.merge({}, objServer))
-
-test('Same initial', function(t) {
-    t.deepEqual(objServer, objClient)
-    t.end()
-})
-
-test('Splice 1', function(t) {
-    var collector = dop.collect()
-    objServer.array.splice(1, 1, 'Mola1')
-
-    t.notDeepEqual(objServer, objClient)
-    makeTest(t, collector, objServer, objClient)
-    t.deepEqual(objServer, objClient)
-    t.end()
-})
-
-test('Splice 2', function(t) {
-    var collector = dop.collect()
-    objServer.array.splice(0, 0, 'Mola2')
-
-    t.notDeepEqual(objServer, objClient)
-    makeTest(t, collector, objServer, objClient)
-    t.deepEqual(objServer, objClient)
-    t.end()
-})
-
-test('Splice 3', function(t) {
-    var collector = dop.collect()
-    objServer.array.splice(100, 100, 'Mola3')
-
-    t.notDeepEqual(objServer, objClient)
-    makeTest(t, collector, objServer, objClient)
-    t.deepEqual(objServer, objClient)
-    t.end()
-})
-
-test('Splice 4', function(t) {
-    var collector = dop.collect()
-    objServer.array.splice(1, 1, 'Mola4')
-
-    t.notDeepEqual(objServer, objClient)
-    makeTest(t, collector, objServer, objClient)
-    t.deepEqual(objServer, objClient)
-    t.end()
-})
-
-test('Splice 5', function(t) {
-    var collector = dop.collect()
-    objServer.array.splice(1, -100, 'Mola5')
-
-    t.notDeepEqual(objServer, objClient)
-    makeTest(t, collector, objServer, objClient)
-    t.deepEqual(objServer, objClient)
-    t.end()
-})
-
-test('Splice 6', function(t) {
-    var collector = dop.collect()
-    objServer.array.splice(-1, 2, 'Mola6')
-
-    t.notDeepEqual(objServer, objClient)
-    makeTest(t, collector, objServer, objClient)
-    t.deepEqual(objServer, objClient)
-    t.end()
-})
-
-test('Swap 1', function(t) {
-    var collector = dop.collect()
-    objServer.array.reverse()
-
-    t.notDeepEqual(objServer, objClient)
-    makeTest(t, collector, objServer, objClient)
-    t.deepEqual(objServer, objClient)
-    t.end()
-})
-
-test('Deleting an subobject on destiny', function(t) {
-    delete objClient.subobject
-    t.notDeepEqual(objServer, objClient)
-
-    var collector = dop.collect()
-    set(objServer.subobject, 'value1', false)
-
-    makeTest(t, collector, objServer, objClient)
-    t.deepEqual(objClient, objServer)
-    t.end()
-})
-
-test('Deleting an array on destiny', function(t) {
-    t.deepEqual(objClient, objServer)
-    delete objClient.array
-    t.notDeepEqual(objServer, objClient)
-
-    var collector = dop.collect()
-    set(objServer.array, 0, 'newValue')
-
-    makeTest(t, collector, objServer, objClient)
-    t.notDeepEqual(objClient.array, objServer.array)
-    t.equal(objClient.array[0], objServer.array[0])
-    t.end()
-})
-
-test('Deleting a subobject on destiny and deleting parent property on origin', function(t) {
-    t.deepEqual(objClient.subobject, objServer.subobject)
-    delete objClient.subobject
-
-    var collector = dop.collect()
-    del(objServer.subobject, 'value1')
-
-    makeTest(t, collector, objServer, objClient)
-    t.deepEqual(objClient.subobject, objServer.subobject)
-
-    t.end()
-})
-
-test('Mutating array in server when objclient does not have that array', function(t) {
-    delete objClient.array
-    t.notDeepEqual(objServer, objClient)
-
-    var collector = dop.collect()
-    objServer.array.unshift({ value2: true })
-
-    makeTest(t, collector, objServer, objClient)
-    t.notEqual(objClient.array.length, objServer.array.length)
-    t.deepEqual(objClient.array[0], objServer.array[0])
-    t.end()
-})
diff --git a/test/setpatchs2.js b/test/setpatchs2.js
deleted file mode 100644
index beb7ee9..0000000
--- a/test/setpatchs2.js
+++ /dev/null
@@ -1,28 +0,0 @@
-var test = require('tape')
-var dopClient = require('./.proxy').create()
-var dopServer = require('./.proxy').create()
-
-function getPatch(collector) {
-    var patch = dopServer.core.getPatch(collector.mutations)
-    var patchServer = dopServer.decode(dopServer.encode(patch))
-    collector.destroy()
-    for (var id in patchServer) return patchServer[id].chunks
-}
-function setPatch(destiny, chunks) {
-    dopClient.core.setPatch(destiny, chunks, dopClient.core.setPatchMutator)
-}
-
-test('Creating a new property with a new object must generate only one mutation in client', function(t) {
-    var objServer = dopServer.register({})
-    var objClient = dopClient.register({})
-    var collectorServer = dopServer.collect()
-    dopServer.set(objServer, 'prop', { new: 'object' })
-
-    var collectorClient = dopClient.collect()
-    setPatch(objClient, getPatch(collectorServer))
-
-    t.equal(collectorClient.mutations.length, 1)
-    t.deepEqual(objServer, objClient)
-    collectorClient.destroy()
-    t.end()
-})
diff --git a/test/sets_dels.js b/test/sets_dels.js
deleted file mode 100644
index 69c1f2f..0000000
--- a/test/sets_dels.js
+++ /dev/null
@@ -1,213 +0,0 @@
-var test = require('tape')
-// require('tabe').createStream( test );
-var dop = require('./.proxy').create()
-var set = dop.set
-var del = dop.del
-var object = dop.register({
-    prop: 'prop',
-    subobject: {}
-})
-var object_id = 1
-var f = function() {}
-var isProxy = typeof Proxy == 'function'
-
-test('Update a property with method set must return the value', function(t) {
-    var ret = set(object, 'prop', 'newvalue')
-    t.equal(ret, 'newvalue')
-    t.end()
-})
-
-test('Add a property with method set must return the value', function(t) {
-    var ret = set(object, 'newprop', 'newpropvalue')
-    t.equal(ret, 'newpropvalue')
-    t.end()
-})
-
-test('Update a property with the same value return the value', function(t) {
-    var ret = set(object, 'prop', 'newvalue')
-    t.equal(ret, 'newvalue')
-    t.end()
-})
-
-test('Update a property no writable return same value but do not change value', function(t) {
-    Object.defineProperty(object, 'nowritable', {
-        value: 1234,
-        configurable: true
-    })
-    var ret = set(object, 'nowritable', 'newvalue')
-    t.equal(ret, 'newvalue')
-    t.equal(object.nowritable, 1234)
-    t.end()
-})
-
-test('Update a property no configurable return same value and change property', function(t) {
-    Object.defineProperty(object, 'noconfigurable', {
-        value: 1234,
-        writable: true
-    })
-    var ret = set(object, 'noconfigurable', 'newvalue')
-    t.equal(ret, 'newvalue')
-    t.equal(object.noconfigurable, 'newvalue')
-    t.end()
-})
-
-test('Update a property as RegExp should not be registered', function(t) {
-    var ret = set(object, 'prop', /regexp/)
-    t.equal(dop.getObjectDop(object.prop), undefined)
-    t.end()
-})
-
-test('Update a property as Date should not be registered', function(t) {
-    var ret = set(object, 'prop', new Date())
-    t.equal(dop.getObjectDop(object.prop), undefined)
-    t.end()
-})
-
-test('Del a property return true and value is deleted', function(t) {
-    var ret = del(object, 'prop')
-    t.equal(ret, true)
-    t.equal(object.prop, undefined)
-    t.end()
-})
-
-test('Del a property that is not defined return false', function(t) {
-    var ret = del(object, 'prop')
-    t.equal(ret, false)
-    t.equal(object.prop, undefined)
-    t.end()
-})
-
-test('Del a property no configurable return false and value do not change', function(t) {
-    var ret = del(object, 'noconfigurable')
-    t.equal(ret, false)
-    t.equal(object.noconfigurable, 'newvalue')
-    t.end()
-})
-
-test('Del a property no writable return true and value is deleted', function(t) {
-    var ret = del(object, 'nowritable')
-    t.equal(ret, true)
-    t.equal(object.nowritable, undefined)
-    t.end()
-})
-
-test('Add a new subobject must convert it as register', function(t) {
-    set(object, 'new', { isregistered: true })
-    var dopobject = dop.getObjectDop(object.new)
-    t.equal(typeof dopobject, 'object')
-    // t.equal(JSON.stringify(dopobject), JSON.stringify([object_id, 'new']));
-    t.end()
-})
-
-test('Add a new subobject that content another register object must create a new one', function(t) {
-    var newregisteredobject = dop.register({ test: 111 })
-    set(object, 'registered', { object: newregisteredobject })
-    var dopobject = dop.getObjectDop(object.registered.object)
-    t.equal(typeof dopobject, 'object')
-    // t.equal(JSON.stringify(dopobject), JSON.stringify([object_id, 'registered', 'object']));
-    t.end()
-})
-
-test('Copying object already registered must create a new one', function(t) {
-    var obj2 = dop.register({ new: { isregistered: true } })
-    set(object, 'new2', obj2.new)
-    t.equal(object.new === object.new2, false)
-    t.deepEqual(object.new, object.new2)
-    t.end()
-})
-
-test("Copying object already registered but that belongs to the same parent won't create a new object", function(t) {
-    set(object, 'new2', object.new)
-    t.equal(object.new === object.new2, true)
-    t.deepEqual(object.new, object.new2)
-    t.end()
-})
-
-test('Copying object already registered into another deep object', function(t) {
-    set(object.subobject, 'new', object.new)
-    var dopobject = dop.getObjectDop(object.subobject.new)
-    t.equal(object.subobject.new === object.new, false)
-    // t.equal(JSON.stringify(dopobject), JSON.stringify([object_id, 'subobject', 'new']));
-    t.end()
-})
-
-test('Update a property seal return same value and change property', function(t) {
-    Object.seal(object)
-    var ret = set(object, 'new', 'newsealvalue')
-    t.equal(ret, 'newsealvalue')
-    t.equal(object.new, 'newsealvalue')
-    t.end()
-})
-
-// behavior has changed in node 8
-// test('Add a property seal return same value and dont add anything', function(t) {
-//     var ret = set(object, 'newseal', 'newsealvalue');
-//     t.equal(ret, 'newsealvalue');
-//     t.equal(object.newseal, undefined);
-//     t.end();
-// });
-
-test('Del a property seal return false and dont delete the property', function(t) {
-    var ret = del(object, 'new')
-    t.equal(ret, false)
-    t.equal(object.new, 'newsealvalue')
-    t.end()
-})
-
-test('Update a property freeze return same value and dont change property', function(t) {
-    Object.freeze(object)
-    var ret = set(object, 'new', 'newfreezevalue')
-    t.equal(ret, 'newfreezevalue')
-    t.equal(object.new, 'newsealvalue')
-    t.end()
-})
-
-test('set() deep option', function(t) {
-    var object = dop.register({})
-    set(object, 'deep', { a: 1, b: { e: true } })
-    set(object, 'nodeep', { a: 1, b: { e: true } }, { deep: false })
-    t.equal(dop.isRegistered(object.deep), true)
-    t.equal(dop.isRegistered(object.nodeep), false)
-    t.deepEqual(object.deep, object.nodeep)
-    t.end()
-})
-
-if (isProxy) {
-    test('Add a object', function(t) {
-        var object_ori = {}
-        var object = dop.register(object_ori)
-        var o = {}
-        var a = []
-        var c = new f()
-        set(object, 'o', o)
-        set(object, 'a', a)
-        set(object, 'c', c)
-        t.notEqual(object, object_ori)
-        t.equal(dop.getObjectTarget(object), object_ori)
-        t.notEqual(object.o, o)
-        t.equal(dop.getObjectTarget(object.o), o)
-        t.notEqual(object.a, a)
-        t.equal(dop.getObjectTarget(object.a), a)
-        t.equal(object.c, c)
-        t.end()
-    })
-
-    test('Add a class', function(t) {
-        var object_ori = new f()
-        var object = dop.register(object_ori)
-        var o = {}
-        var a = []
-        var c = new f()
-        set(object, 'o', o)
-        set(object, 'a', a)
-        set(object, 'c', c)
-        t.notEqual(object, object_ori)
-        t.equal(dop.getObjectTarget(object), object_ori)
-        t.notEqual(object.o, o)
-        t.equal(dop.getObjectTarget(object.o), o)
-        t.notEqual(object.a, a)
-        t.equal(dop.getObjectTarget(object.a), a)
-        t.equal(object.c, c)
-        t.end()
-    })
-}
diff --git a/test/type_delete.js b/test/type_delete.js
new file mode 100644
index 0000000..998e42d
--- /dev/null
+++ b/test/type_delete.js
@@ -0,0 +1,100 @@
+import test from 'ava'
+import { decode, TYPE } from '../src'
+import { testEncodeDecode, testPatchUnpatch } from './utils'
+const DELETE = TYPE.Delete()
+
+test('Valid type', function (t) {
+    const patch = { convert: DELETE }
+    const expected = { convert: { $d: 0 } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('Escape', function (t) {
+    const patch = { escape: { $d: 0 } }
+    const expected = { escape: { $escape: { $d: 0 } } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('Ignore', function (t) {
+    const patch = { ignore: { $d: 0, $other: 0 } }
+    const expected = { ignore: { $d: 0, $other: 0 } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('$escaping', function (t) {
+    const patch = {
+        convert: DELETE,
+        escape: { $d: 0 },
+    }
+    const expected = {
+        convert: { $d: 0 },
+        escape: { $escape: { $d: 0 } },
+    }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('This should not be escaped because $d has another valid prop', function (t) {
+    const patch = {
+        convert: DELETE,
+        ignored: { $d: 0, $escape: 0 },
+    }
+    const expected = {
+        convert: { $d: 0 },
+        ignored: { $d: 0, $escape: 0 },
+    }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('This should not be escaped because $d has another valid prop 2', function (t) {
+    const patch = {
+        convert: DELETE,
+        escape: { $escape: 0, $d: DELETE },
+    }
+    const expected = {
+        convert: { $d: 0 },
+        escape: { $escape: 0, $d: { $d: 0 } },
+    }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('This should not be escaped because $d is not an number', function (t) {
+    const patch = {
+        convert: DELETE,
+        escape: { $d: 'string' },
+    }
+    const expected = {
+        convert: { $d: 0 },
+        escape: { $d: 'string' },
+    }
+    testEncodeDecode(t, patch, expected) // testEncodeDecode(t, patch, expected) // Not sure why EJSON is still escaping strings
+})
+
+test('Invalid format, should not be escaped', function (t) {
+    const patch = { convert: { $d: 1 } }
+    const expected = { convert: { $d: 1 } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('Decode alone', function (t) {
+    const patch = {
+        convert: { $d: 0 },
+        string: { $d: 'string' },
+        escape: { $escape: { $d: 0 } },
+        ignore: { $escape: { $d: 0 }, two: { $d: 0 } },
+    }
+    const expected = {
+        convert: DELETE,
+        string: { $d: 'string' },
+        escape: { $d: 0 },
+        ignore: { $escape: DELETE, two: DELETE },
+    }
+    t.deepEqual(decode(patch), expected)
+})
+
+test('patch', function (t) {
+    const target = { value: 12345 }
+    const patch = { value: TYPE.Delete() }
+    const expected = {}
+
+    testPatchUnpatch({ t, target, patch, expected })
+})
diff --git a/test/type_multi.js b/test/type_multi.js
new file mode 100644
index 0000000..f2ca4ec
--- /dev/null
+++ b/test/type_multi.js
@@ -0,0 +1,73 @@
+import test from 'ava'
+import { applyPatch } from '../src'
+import { TYPE } from '../src'
+import { testEncodeDecode, testPatchUnpatch } from './utils'
+
+test('Valid type', function (t) {
+    const patch = { convert: TYPE.Multi(0, 1) }
+    const expected = { convert: { $m: [0, 1] } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('Inner encode', function (t) {
+    const patch = { convert: TYPE.Multi(0, 1, TYPE.Delete()) }
+    const expected = { convert: { $m: [0, 1, { $d: 0 }] } }
+    const { encoded, decoded } = testEncodeDecode(t, patch, expected)
+})
+
+test('Escape', function (t) {
+    const patch = { escape: { $m: [0, 1] } }
+    const expected = { escape: { $escape: { $m: [0, 1] } } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('Ignore', function (t) {
+    const patch = { ignore: { $m: { a: 2 } } }
+    const expected = { ignore: { $m: { a: 2 } } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('API', function (t) {
+    const target = { array: ['a', 'b'] }
+    const array = target.array
+    const patch = {
+        array: TYPE.Multi(
+            TYPE.Splice(0, 0, 'c'),
+            TYPE.Swap(0, 2),
+            TYPE.Swap(1, 2)
+        ),
+    }
+    const expected = { array: ['b', 'c', 'a'] }
+    const { unpatch, result, mutations } = testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+    })
+    t.is(mutations.length, 1)
+    t.is(result.array, target.array)
+    t.is(array, target.array)
+    t.true(unpatch.array instanceof TYPE.Multi)
+    t.is(unpatch.array.values.length, 3)
+})
+
+test('Deleting', function (t) {
+    const target = { array: false }
+    const patch = { array: TYPE.Multi(TYPE.Delete()) }
+    const expected = {}
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('Adding array and swaping', function (t) {
+    const target = { array: 1234 }
+    const patch = { array: TYPE.Multi([1, 2], TYPE.Swap(0, 1)) }
+    const expected = { array: [2, 1] }
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('Undefined before applying Multi', function (t) {
+    const target = {}
+    const patch = { array: TYPE.Multi(true) }
+    const expected = { array: true }
+    testPatchUnpatch({ t, target, patch, expected })
+})
diff --git a/test/type_replace.js b/test/type_replace.js
new file mode 100644
index 0000000..8cf9370
--- /dev/null
+++ b/test/type_replace.js
@@ -0,0 +1,158 @@
+import test from 'ava'
+import { decode, TYPE } from '../src'
+import { testEncodeDecode, testPatchUnpatch } from './utils'
+
+test('Valid type', function (t) {
+    const patch = { convert: TYPE.Replace({ hello: 'world' }) }
+    const expected = { convert: { $e: { hello: 'world' } } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('Escape', function (t) {
+    const patch = { escape: { $e: 1 } }
+    const expected = { escape: { $escape: { $e: 1 } } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('Ignore', function (t) {
+    const patch = { ignore: { $e: 1, $other: 1 } }
+    const expected = { ignore: { $e: 1, $other: 1 } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('$escaping', function (t) {
+    const patch = {
+        convert: TYPE.Replace([1, 2, 3]),
+        escape: { $e: 1 },
+    }
+    const expected = {
+        convert: { $e: [1, 2, 3] },
+        escape: { $escape: { $e: 1 } },
+    }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('This should not be escaped because $e has another valid prop', function (t) {
+    const patch = {
+        convert: TYPE.Replace([1, 2, 3]),
+        ignored: { $e: [1, 2, 3], $escape: [1, 2, 3] },
+    }
+    const expected = {
+        convert: { $e: [1, 2, 3] },
+        ignored: { $e: [1, 2, 3], $escape: [1, 2, 3] },
+    }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('This should not be escaped because $e has another valid prop 2', function (t) {
+    const patch = {
+        convert: TYPE.Replace([1, 2, 3]),
+        escape: { $escape: [1, 2, 3], $e: TYPE.Replace([1, 2, 3]) },
+    }
+    const expected = {
+        convert: { $e: [1, 2, 3] },
+        escape: { $escape: [1, 2, 3], $e: { $e: [1, 2, 3] } },
+    }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('Decode alone', function (t) {
+    const patch = {
+        convert: { $e: [1, 2, 3] },
+        string: { $e: 'string' },
+        escape: { $escape: { $e: [1, 2, 3] } },
+        ignore: {
+            $escape: { $e: [1, 2, 3] },
+            two: { $e: [1, 2, 3] },
+        },
+    }
+    const expected = {
+        convert: TYPE.Replace([1, 2, 3]),
+        string: TYPE.Replace('string'),
+        escape: { $e: [1, 2, 3] },
+        ignore: {
+            $escape: TYPE.Replace([1, 2, 3]),
+            two: TYPE.Replace([1, 2, 3]),
+        },
+    }
+    t.deepEqual(decode(patch), expected)
+})
+
+test('patch array', function (t) {
+    const target = { value: { a: 1, b: 2 } }
+    const patch = { value: TYPE.Replace([1, 2, 3]) }
+    const expected = { value: [1, 2, 3] }
+
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('patch object', function (t) {
+    const target = { value: { a: 1, b: 2 } }
+    const patch = { value: TYPE.Replace({ c: 3 }) }
+    const expected = { value: { c: 3 } }
+
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('patch should replace the complete object', function (t) {
+    const obj_to_replace = { c: 3 }
+    const target = { value: { a: 1, b: 2 } }
+    const patch = { value: TYPE.Replace(obj_to_replace) }
+    const expected = { value: { c: 3 } }
+    const copyvalue = target.value
+
+    testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+        reverse: false,
+        encodedecode: false,
+    })
+    t.is(obj_to_replace, target.value)
+    t.not(copyvalue, target.value)
+})
+
+test('testing that last test works correctly without replace', function (t) {
+    const target = { value: { a: 1, b: 2 } }
+    const patch = { value: { c: 3 } }
+    const expected = { value: { a: 1, b: 2, c: 3 } }
+
+    const copyvalue = target.value
+
+    testPatchUnpatch({ t, target, patch, expected })
+    t.is(copyvalue, target.value)
+    t.deepEqual(copyvalue, target.value)
+})
+
+test('replace array', function (t) {
+    const target = { value: { a: 1, b: 2 } }
+    const patch = TYPE.Replace([1, 2])
+    const expected = [1, 2]
+
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('same behavior as replace array', function (t) {
+    const target = { value: { a: 1, b: 2 } }
+    const patch = [1, 2]
+    const expected = [1, 2]
+
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('inner types', function (t) {
+    const target = { value: { a: 1, b: 2 } }
+    const patch = { value: TYPE.Replace({ a: 1, b: TYPE.Swap(1, 2) }) }
+    const expected = { value: { a: 1, b: TYPE.Swap(1, 2) } }
+
+    testPatchUnpatch({ t, target, patch, expected, reverse: false })
+})
+
+test('replace when is not defined yet', function (t) {
+    const target = {}
+    const patch = { value: TYPE.Replace({ a: 1 }) }
+    const expected = { value: { a: 1 } }
+
+    testPatchUnpatch({ t, target, patch, expected })
+})
diff --git a/test/type_splice.js b/test/type_splice.js
new file mode 100644
index 0000000..7b0a2af
--- /dev/null
+++ b/test/type_splice.js
@@ -0,0 +1,227 @@
+import test from 'ava'
+import { applyPatch } from '../src'
+import { TYPE } from '../src'
+import { testEncodeDecode, testPatchUnpatch } from './utils'
+
+test('Valid type', function (t) {
+    const patch = { convert: TYPE.Splice(0, 1, 'world') }
+    const expected = { convert: { $s: [0, 1, 'world'] } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('Escape', function (t) {
+    const patch = { escape: { $s: [0, 1] } }
+    const expected = { escape: { $escape: { $s: [0, 1] } } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('Ignore', function (t) {
+    const patch = { ignore: { $s: { a: 2 } } }
+    const expected = { ignore: { $s: { a: 2 } } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('API', function (t) {
+    const target = { array: ['a'] }
+    const patch = { array: TYPE.Splice(1, 0, 'b') }
+    const expected = { array: ['a', 'b'] }
+    const { unpatch, result, mutations } = testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+    })
+    t.is(mutations.length, 1)
+    t.is(result.array, target.array)
+    t.true(unpatch.array instanceof TYPE.Splice)
+    t.deepEqual(unpatch.array.args, [1, 1])
+})
+
+test('Patching a non array must do nothing', function (t) {
+    const target = { array: 1234 }
+    const patch = { array: TYPE.Splice(0, 0, 5678) }
+    const expected = { array: 1234 }
+    const { mutations } = testPatchUnpatch({ t, target, patch, expected })
+    t.is(mutations.length, 0)
+})
+
+test('removing and adding', function (t) {
+    const target = { array: ['a', 'b', 'c'] }
+    const patch = { array: TYPE.Splice(0, 1, 'd') }
+    const expected = { array: ['d', 'b', 'c'] }
+    const { mutations } = testPatchUnpatch({ t, target, patch, expected })
+    t.is(mutations.length, 1)
+})
+
+test('removing', function (t) {
+    const target = { array: ['a', 'b', 'c'] }
+    const patch = { array: TYPE.Splice(0, 1) }
+    const expected = { array: ['b', 'c'] }
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('removing middle', function (t) {
+    const target = { array: ['a', 'b', 'c'] }
+    const patch = { array: TYPE.Splice(1, 1) }
+    const expected = { array: ['a', 'c'] }
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('adding', function (t) {
+    const target = { array: ['a', 'b', 'c'] }
+    const patch = { array: TYPE.Splice(3, 0, 'd') }
+    const expected = { array: ['a', 'b', 'c', 'd'] }
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('negative', function (t) {
+    const target = { array: ['angel', 'clown', 'mandarin', 'sturgeon'] }
+    const patch = { array: TYPE.Splice(-2, 1) }
+    const expected = { array: ['angel', 'clown', 'sturgeon'] }
+    testPatchUnpatch({ t, target, patch, expected, reverse: false })
+})
+
+function testAgainstNative(t, original, params) {
+    const native = original.slice(0)
+    Array.prototype.splice.apply(native, params)
+
+    const target = original.slice(0)
+    const patch = TYPE.Splice.apply(null, params)
+    // console.log({ original, params })
+    const { unpatch } = applyPatch(target, patch)
+    t.deepEqual(target, native)
+    // console.log({ result: target })
+    applyPatch(target, unpatch)
+    t.deepEqual(target, original)
+    // console.log({ result: target })
+}
+
+test('inner types', function (t) {
+    const target = { value: [0, 1] }
+    const patch = { value: TYPE.Splice(2, 0, TYPE.Swap(1, 2)) }
+    const expected = { value: [0, 1, TYPE.Swap(1, 2)] }
+
+    testPatchUnpatch({ t, target, patch, expected, reverse: false })
+})
+
+test('1/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [0, 0]
+    testAgainstNative(t, original, params)
+})
+
+test('2/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [0, 1]
+    testAgainstNative(t, original, params)
+})
+
+test('3/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [0, -1]
+    testAgainstNative(t, original, params)
+})
+
+test('4/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [1, 0]
+    testAgainstNative(t, original, params)
+})
+test('5/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [1, 1]
+    testAgainstNative(t, original, params)
+})
+test('6/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [1, -1]
+    testAgainstNative(t, original, params)
+})
+
+test('7/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [-1, 0]
+    testAgainstNative(t, original, params)
+})
+
+test('8/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [-1, 1]
+    testAgainstNative(t, original, params)
+})
+
+test('9/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [-1, -1]
+    testAgainstNative(t, original, params)
+})
+
+test('10/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [0, 0, 'last']
+    testAgainstNative(t, original, params)
+})
+
+test('11/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [0, 1, 'last']
+    testAgainstNative(t, original, params)
+})
+
+test('12/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [0, -1, 'last']
+    testAgainstNative(t, original, params)
+})
+
+test('13/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [1, 0, 'last']
+    testAgainstNative(t, original, params)
+})
+test('14/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [1, 1, 'last']
+    testAgainstNative(t, original, params)
+})
+test('15/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [1, -1, 'last']
+    testAgainstNative(t, original, params)
+})
+
+test('16/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [-1, 0, 'last']
+    testAgainstNative(t, original, params)
+})
+
+test('17/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [-1, 1, 'last']
+    testAgainstNative(t, original, params)
+})
+
+test('18/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [-1, -1, 'last']
+    testAgainstNative(t, original, params)
+})
+
+test('19/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [-2, 0, 'last', 'prelast']
+    testAgainstNative(t, original, params)
+})
+
+test('20/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [-2, 1, 'last', 'prelast']
+    testAgainstNative(t, original, params)
+})
+
+test('21/ vs Array.splice', function (t) {
+    const original = ['angel', 'clown', 'mandarin', 'sturgeon']
+    const params = [-2, -1, 'last', 'prelast']
+    testAgainstNative(t, original, params)
+})
diff --git a/test/type_swap.js b/test/type_swap.js
new file mode 100644
index 0000000..cd00e69
--- /dev/null
+++ b/test/type_swap.js
@@ -0,0 +1,77 @@
+import test from 'ava'
+import { TYPE, applyPatch } from '../src'
+import { testEncodeDecode, testPatchUnpatch } from './utils'
+
+test('Valid type', function (t) {
+    const patch = { convert: TYPE.Swap(0, 1) }
+    const expected = { convert: { $w: [0, 1] } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('Escape', function (t) {
+    const patch = { escape: { $w: [0, 1] } }
+    const expected = { escape: { $escape: { $w: [0, 1] } } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('Ignore', function (t) {
+    const patch = { ignore: { $w: { a: 2 } } }
+    const expected = { ignore: { $w: { a: 2 } } }
+    testEncodeDecode(t, patch, expected)
+})
+
+test('API', function (t) {
+    const target = { array: ['a', 'b'] }
+    const patch = { array: TYPE.Swap(0, 1) }
+    const expected = { array: ['b', 'a'] }
+    const { unpatch, result, mutations } = testPatchUnpatch({
+        t,
+        target,
+        patch,
+        expected,
+    })
+    t.is(mutations.length, 1)
+    t.is(result.array, target.array)
+    t.true(unpatch.array instanceof TYPE.Swap)
+    t.deepEqual(unpatch.array.args, [1, 0])
+})
+
+test('Patching a non array must do nothing', function (t) {
+    const target = { array: 1234 }
+    const patch = { array: TYPE.Swap(0, 1) }
+    const expected = { array: 1234 }
+    const { mutations } = testPatchUnpatch({ t, target, patch, expected })
+    t.is(mutations.length, 0)
+})
+
+test('Multiple mutations', function (t) {
+    const target = { array: ['a', 'b', 'c', 'd'] }
+    const patch = { array: TYPE.Swap(0, 2, 1, 3) }
+    const expected = { array: ['c', 'd', 'a', 'b'] }
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('Combining swaps', function (t) {
+    const target = { array: ['a', 'b', 'c'] }
+    const patch = { array: TYPE.Swap(0, 1, 2, 1) }
+    const expected = { array: ['b', 'c', 'a'] }
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('Repeating swaps', function (t) {
+    const target = { array: ['a', 'b'] }
+    const patch = { array: TYPE.Swap(0, 1, 1, 0) }
+    const expected = { array: ['a', 'b'] }
+    testPatchUnpatch({ t, target, patch, expected })
+})
+
+test('Swaping do not mutate inner objects, must be the same instances', function (t) {
+    const target = { array: [{ a: 1 }, { b: 2 }] }
+    const a = target.array[0]
+    const b = target.array[1]
+    const patch = { array: TYPE.Swap(0, 1) }
+    const expected = { array: [{ b: 2 }, { a: 1 }] }
+    testPatchUnpatch({ t, target, patch, expected, reverse: false })
+    t.is(a, target.array[1])
+    t.is(b, target.array[0])
+})
diff --git a/test/undoredo.js b/test/undoredo.js
deleted file mode 100644
index d49ced7..0000000
--- a/test/undoredo.js
+++ /dev/null
@@ -1,238 +0,0 @@
-var test = require('tape');
-var dop = require('./.proxy').create();
-var set = dop.set;
-var del = dop.del;
-
-
-
-
-function makeTest(t, snapshot, original, object, mutated) {
-
-    var object_id = dop.getObjectId(object)
-    var target = dop.getObjectTarget(object)
-    var unpatch = snapshot.getUnpatch()[object_id].chunks
-    var patch = snapshot.getPatch()[object_id].chunks
-
-
-    unpatch = dop.decode(dop.encode(unpatch))
-    // console.log('UNDO', unpatch.length, JSON.stringify(unpatch))
-    patch = dop.decode(dop.encode(patch))
-    // console.log('REDO', patch.length, JSON.stringify(patch))
-
-    // Undo
-    snapshot.undo()
-    t.deepEqual(target, original, 'undo 1')
-   
-    // Redo
-    snapshot.redo()
-    t.deepEqual(target, mutated, 'redo 1')
-
-    // Undo
-    snapshot.undo()
-    snapshot.undo()
-    t.deepEqual(target, original, 'undo 2')
-   
-    // Redo
-    snapshot.redo()
-    snapshot.redo()
-    t.deepEqual(target, mutated, 'redo 2')
-
-    t.end()
-}
-
-
-
-
-
-
-//todo
-// set
-// setarray length
-// del
-// newprop
-// reverse
-// splice
-// sortby
-
-
-
-
-test('Set array', function(t) {
-
-    var object = dop.register({dos:{a:1,b:2,c:3}})
-    var original = dop.util.clone(object)
-    var collector = dop.collect()
-    set(object, 'dos', {e:4})
-    set(object, 'three', undefined)
-    var snapshot = collector.emit()
-    var mutated = dop.util.clone(object)
-    
-    makeTest(t, snapshot, original, object, mutated) 
-});
-
-
-test('Reverse', function(t) {
-
-    var object = dop.register({dos:["A","B","C"]})
-    var original = dop.util.clone(object)
-    var collector = dop.collect()
-    object.dos.reverse();   
-    set(object, 'dos', [1,2,3])
-    object.dos.reverse();   
-    var snapshot = collector.emit()
-    var mutated = dop.util.clone(object)
-    
-    makeTest(t, snapshot, original, object, mutated) 
-});
-
-
-test('set length array', function(t) {
-
-    var object = dop.register({array:[1,2,'3',4,5]})
-    var original = dop.util.clone(object)
-    var collector = dop.collect()
-    set(object.array, '10', 'text')
-    var snapshot = collector.emit()
-    var mutated = dop.util.clone(object)
-
-    makeTest(t, snapshot, original, object, mutated) 
-});
-
-
-
-test('set a random property in an array', function(t) {
-
-    var object = dop.register({array:[1,2,'3',4,5]})
-    var original = dop.util.clone(object)
-    var collector = dop.collect()
-    set(object, 'hello', 'world')
-    set(object.array, 'hello', 'world')
-    var snapshot = collector.emit()
-    var mutated = dop.util.clone(object)
-
-    makeTest(t, snapshot, original, object, mutated) 
-});
-
-
-
-test('set length sub array', function(t) {
-
-    var object = dop.register({array:[1,2,'3',4,5]})
-    var original = dop.util.clone(object)
-    var collector = dop.collect()
-    set(object, 'array', {caca:1})
-    var snapshot = collector.emit()
-    var mutated = dop.util.clone(object)  
-
-    makeTest(t, snapshot, original, object, mutated) 
-})
-
-
-
-test('array to object', function(t) {
-
-    var object = dop.register({array:[1,2,'3',4,5]})
-    var original = dop.util.clone(object)
-    var collector = dop.collect()
-    object.array.reverse();   
-    object.array.sort();   
-    set(object, 'array', {caca:1})
-    var snapshot = collector.emit()
-    var mutated = dop.util.clone(object)
-    
-    makeTest(t, snapshot, original, object, mutated) 
-});
-
-test('object to array', function(t) {
-
-    var object = dop.register({obj:{caca:1}})
-    var original = dop.util.clone(object)
-    var collector = dop.collect()
-    set(object, 'obj', [1,2,'3',4,5])
-    var snapshot = collector.emit()
-    var mutated = dop.util.clone(object)
-    
-    makeTest(t, snapshot, original, object, mutated) 
-});
-
-test('array two mutation', function(t) {
-
-    var object = dop.register({array:[{},"dos","tres","cuatro",{five:"five"}]})
-    var original = dop.util.clone(object)
-    var collector = dop.collect()
-    set(object.array[0], 'prop', 'value')
-    set(object.array, 4, 'five')
-    object.array.reverse();   
-    object.array.sort();   
-    var snapshot = collector.emit()
-    var mutated = dop.util.clone(object)
-    makeTest(t, snapshot, original, object, mutated) 
-});
-
-test('array deep mutation', function(t) {
-
-    var object = dop.register({array:[{p:[{p:[{}]}]},"dos"]})
-    var original = dop.util.clone(object)
-    var collector = dop.collect()
-    set(object.array[0].p[0].p[0], 'prop', 'value')
-    var snapshot = collector.emit()
-    var mutated = dop.util.clone(object)
-    
-    makeTest(t, snapshot, original, object, mutated) 
-});
-
-
-test('Bunch of mutations', function(t) {
-
-    var object = dop.register({dos:[1,2,3]})
-    var original = dop.util.clone(object)
-    var collector = dop.collect()
-    set(object, 'one', 11)
-    set(object.dos, 4, 'Changed Again');
-    object.dos.push({new:"Item"});
-    set(object.dos[5], 'Other', 'property');
-    object.dos.sort();
-    object.dos.pop();
-    set(object, 'tres', 3)
-    object.dos.push({new2:"Item2"});
-    set(object.dos[5], 'Other2', 'property2');
-    object.dos.reverse();   
-    // set(object, 'dos', '')
-    var snapshot = collector.emit()
-    var mutated = dop.util.clone(object)
-    
-    makeTest(t, snapshot, original, object, mutated) 
-});
-
-
-test('From string to array and mutations', function(t) {
-
-    var object = dop.register({dos:[1,2,3]})
-    var original = dop.util.clone(object)
-    var collector = dop.collect()
-    object.dos.splice(1,1) 
-    set(object, 'new', 'n') 
-    object.dos.reverse()
-    set(object, 'dos', [4,5,7])
-    object.dos.reverse()
-    del(object, 'dos') //{"dos":""}
-    var snapshot = collector.emit()
-    var mutated = dop.util.clone(object)
-    
-    makeTest(t, snapshot, original, object, mutated) 
-});
-
-
-
-
-test('Negative splice', function(t) {
-
-    var object = dop.register({dos:[1,2,3]})
-    var original = dop.util.clone(object)
-    var collector = dop.collect()
-    object.dos.splice(1,-1,'!') 
-    var snapshot = collector.emit()
-    var mutated = dop.util.clone(object)
-    
-    makeTest(t, snapshot, original, object, mutated) 
-});
diff --git a/test/utils.js b/test/utils.js
new file mode 100644
index 0000000..7ebbf91
--- /dev/null
+++ b/test/utils.js
@@ -0,0 +1,100 @@
+import { applyPatch, producePatch, encode, decode, merge } from '../src'
+import { getNewPlain } from '../src/util/getset'
+import { isPlainObject, isArray, isFunction } from '../src/util/is'
+
+function newDate(d = new Date().getDate()) {
+    const date = new Date(d)
+    date.toISOString = () => date
+    date.replace = () => date
+    return date
+}
+
+function isInteger(number) {
+    return (
+        typeof number === 'number' &&
+        isFinite(number) &&
+        Math.floor(number) === number
+    )
+}
+
+function encodeDecode(patch, encodedecode = true, serialize = true) {
+    return encodedecode
+        ? decode(serializeDeserialize(encode(patch), serialize))
+        : patch
+}
+
+function serializeDeserialize(patch, serialize = true) {
+    return serialize ? JSON.parse(JSON.stringify(patch)) : patch
+}
+
+function testEncodeDecode(
+    t,
+    patch,
+    expected,
+    reverse = true,
+    serialize = true
+) {
+    const encoded = serializeDeserialize(encode(patch), serialize)
+    t.deepEqual(expected, encoded)
+    t.not(patch, encoded)
+    const decoded = decode(serializeDeserialize(encoded, serialize))
+    if (reverse) {
+        t.deepEqual(patch, decoded)
+        t.not(encoded, decoded)
+    }
+    return { encoded, decoded }
+}
+
+function testPatchUnpatch({
+    t,
+    target,
+    patch,
+    fnpatch,
+    expected,
+    reverse = true,
+    reversefn = true,
+    encodedecode = true,
+    serialize = true,
+}) {
+    let patch2
+    const copytarget = merge(isArray(target) ? [] : {}, target)
+
+    const cloned = getNewPlain(target)
+    const output = applyPatch(
+        target,
+        encodeDecode(patch, encodedecode, serialize)
+    )
+    const { unpatch, mutations, result } = output
+    if (isPlainObject(result) && isPlainObject(target)) {
+        t.is(target, result)
+    }
+    target = result
+    t.deepEqual(target, expected)
+    if (reverse) {
+        const output2 = applyPatch(
+            target,
+            encodeDecode(unpatch, encodedecode, serialize)
+        )
+        t.deepEqual(output2.result, cloned, '(Unpatching)')
+    }
+
+    // producePatch
+    if (isFunction(fnpatch)) {
+        const resu = producePatch(copytarget, fnpatch)
+        patch2 = resu.patch
+        // console.log(resu.mutations.length)
+        testPatchUnpatch({
+            t,
+            target: copytarget,
+            patch: patch2,
+            expected,
+            reverse: reverse && reversefn,
+            encodedecode,
+            serialize,
+        })
+    }
+
+    return { unpatch, mutations, result, patch2 }
+}
+
+module.exports = { newDate, isInteger, testEncodeDecode, testPatchUnpatch }
diff --git a/test/zarrays.js b/test/zarrays.js
deleted file mode 100644
index 8e89c89..0000000
--- a/test/zarrays.js
+++ /dev/null
@@ -1,295 +0,0 @@
-
-var dop = require('./.proxy').create();
-var Combinatorics = require('js-combinatorics');
-var test = require('tape');
-// // require('tabe').createStream( test );
-
-
-
-function gify(obj) {
-    this.myClassProperty=123;
-    return JSON.stringify(obj);
-}
-var isPojoObject = dop.isPojoObject;
-
-
-
-test('for (i in ...) must return only array values', function(t) {
-    var object = dop.register({array:array.slice(0)});
-    var register = object.array;
-    t.equal(gify(Object.keys(register)), gify(Object.keys(array)));
-    t.end();
-});
-
-
-test('Splice', function(t) {
-    argsCases.forEach(function(paramsCase) {
-        var object = dop.register({array:array.slice(0)});
-        var register = object.array;
-        var original = array.slice(0);
-        var description = JSON.stringify(paramsCase);
-        t.equal(gify(register.splice.apply(register, paramsCase)), gify(original.splice.apply(original, paramsCase)), 'output case: '+description);
-        t.equal(gify(original), gify(register), 'stringify case: '+description);
-        t.deepEqual(original,register, 'deepEqual case: '+description);
-
-        for (var index=0,total=register.length; index<total; ++index) {
-            var item = register[index];
-            if (isPojoObject(item)) {
-
-                if (dop.isRegistered(item)) {
-                    var property = dop.getObjectProperty(item);
-                    t.equal(Number(property), Number(index), 'correct path for subobject: '+index + ', Case:'+description);
-                }
-                else if (item.constructor === Object)
-                    t.equal(false, true, 'object not registered: '+index + ', Case:'+description + ' ' + gify(item));
-            }
-        }
-    });
-    t.end();
-});
-
-
-
-test('Shift', function(t) {
-    var object = dop.register({array:array.slice(0)});
-    var register = object.array;
-    var original = array.slice(0);
-    do {
-        var description = original.length;
-        t.equal(gify(register.shift.apply(register)), gify(original.shift.apply(original)), 'output case: '+description);
-        t.equal(gify(original), gify(register), 'stringify case: '+description);
-        t.deepEqual(original,register, 'deepEqual case: '+description);
-        for (var index=0,total=register.length; index<total; ++index) {
-            var item = register[index];
-            if (isPojoObject(item)) {
-
-                if (dop.isRegistered(item)) {
-                    var property = dop.getObjectProperty(item);
-                    t.equal(Number(property), Number(index), 'correct path for subobject: '+index + ', Case:'+description);
-                }
-                else if (item.constructor === Object)
-                    t.equal(false, true, 'object not registered: '+index + ', Case:'+description + ' ' + gify(item));
-            }
-        }
-    } while(original.length>0);
-
-    t.end();
-});
-
-
-
-test('Pop', function(t) {
-    var object = dop.register({array:array.slice(0)});
-    var register = object.array;
-    var original = array.slice(0);
-    do {
-        var description = original.length;
-        t.equal(gify(register.pop.apply(register)), gify(original.pop.apply(original)), 'output case: '+description);
-        t.equal(gify(original), gify(register), 'stringify case: '+description);
-        t.deepEqual(original,register, 'deepEqual case: '+description);
-        for (var index=0,total=register.length; index<total; ++index) {
-            var item = register[index];
-            if (isPojoObject(item)) {
-
-                if (dop.isRegistered(item)) {
-                    var property = dop.getObjectProperty(item);
-                    t.equal(Number(property), Number(index), 'correct path for subobject: '+index + ', Case:'+description);
-                }
-                else if (item.constructor === Object)
-                    t.equal(false, true, 'object not registered: '+index + ', Case:'+description + ' ' + gify(item));
-            }
-        }
-    } while(original.length>0);
-
-    t.end();
-});
-
-
-
-test('Push', function(t) {
-    argsCases.forEach(function(paramsCase) {
-        var object = dop.register({array:array.slice(0)});
-        var register = object.array;
-        var original = array.slice(0);
-        var description = JSON.stringify(paramsCase);
-        t.equal(gify(register.push.apply(register, paramsCase)), gify(original.push.apply(original, paramsCase)), 'output case: '+description);
-        for (var index=0,total=register.length; index<total; ++index) {
-            var item = register[index];
-            if (isPojoObject(item)) {
-
-                if (dop.isRegistered(item)) {
-                    var property = dop.getObjectProperty(item);
-                    t.equal(Number(property), Number(index), 'correct path for subobject: '+index + ', Case:'+description);
-                }
-                else if (item.constructor === Object)
-                    t.equal(false, true, 'object not registered: '+index + ', Case:'+description + ' ' + gify(item));
-            }
-        }
-    });
-    t.end();
-});
-
-
-
-
-test('Unshift', function(t) {
-    argsCases.forEach(function(paramsCase) {
-        var object = dop.register({array:array.slice(0)});
-        var register = object.array;
-        var original = array.slice(0);
-        var description = JSON.stringify(paramsCase);
-        t.equal(gify(register.unshift.apply(register, paramsCase)), gify(original.unshift.apply(original, paramsCase)), 'output case: '+description);
-        t.equal(gify(original), gify(register), 'stringify case: '+description);
-        t.deepEqual(original,register, 'deepEqual case: '+description);
-
-        for (var index=0,total=register.length; index<total; ++index) {
-            var item = register[index];
-            if (isPojoObject(item)) {
-
-                if (dop.isRegistered(item)) {
-                    var property = dop.getObjectProperty(item);
-                    t.equal(Number(property), Number(index), 'correct path for subobject: '+index + ', Case:'+description);
-                }
-                else if (item.constructor === Object)
-                    t.equal(false, true, 'object not registered: '+index + ', Case:'+description + ' ' + gify(item));
-            }
-        }
-    });
-    t.end();
-});
-
-
-test('Reverse', function(t) {
-    var object = dop.register({array:array.slice(0)});
-    var register = object.array;
-    var original = array.slice(0);
-    t.equal(gify(register.reverse()), gify(original.reverse()), 'output case');
-    t.equal(gify(original), gify(register), 'stringify case');
-    t.deepEqual(original,register, 'deepEqual case');
-    for (var index=0,total=register.length; index<total; ++index) {
-        var item = register[index];
-        if (isPojoObject(item)) {
-            dop.getObjectPath(item);
-            if (dop.isRegistered(item)) {
-                var property = dop.getObjectProperty(item);
-                t.equal(Number(property), Number(index), 'correct path for subobject: '+index + ', Case:');
-            }
-            else if (item.constructor === Object)
-                t.equal(false, true, 'object not registered: '+index + ', Case:' + ' ' + gify(item));
-        }
-    }
-    t.end();
-});
-
-
-
-
-
-
-test('Sort', function(t) {
-
-    var arrayOriginal;
-    var copy;
-    var swaps;
-    var compareFunctions = [
-        undefined,
-        function(a,b) {return a-b},
-        function(a,b) {return b-a},
-        function(a,b) {return a>b},
-        function(a,b) {return b>a},
-        function(a,b) {return a===b},
-        function(a,b) {return 1},
-        function(a,b) {return -1},
-        function(a,b) {return 0},
-        function(a,b) {
-            if (a>b) return 1;
-            if (b>a) return -1;
-            return 0;
-        },
-        function(a,b) {
-            if (String(a)>String(b)) return 1;
-            if (String(b)>String(a)) return -1;
-            return 0;
-        }
-    ];
-
-
-
-    compareFunctions.forEach(function(compareFunction) {
-        var arrayOriginal = types.slice(0);
-        var copy = arrayOriginal.slice(0);
-        arrayOriginal.sort(compareFunction);
-        dop.core.sortDiff(arrayOriginal, copy);
-        t.equal(gify(arrayOriginal), gify(copy), 'stringify');
-        t.deepEqual(arrayOriginal, copy, 'deepEqual function:'+compareFunction);
-        var objectRegistered = dop.register({array:array.slice(0)});
-        arrayOriginal = objectRegistered.array;
-        arrayOriginal[13] = true;
-        copy = arrayOriginal.slice(0);
-        arrayOriginal.sort(compareFunction);
-        dop.core.sortDiff(arrayOriginal, copy);
-        t.equal(gify(arrayOriginal), gify(copy), 'stringify');
-        t.deepEqual(arrayOriginal, copy, 'deepEqual function:'+compareFunction);
-        for (var index=0,total=arrayOriginal.length; index<total; ++index) {
-            var item = arrayOriginal[index];
-            if (isPojoObject(item)) {
-
-                if (dop.isRegistered(item)) {
-                    dop.getObjectPath(item);
-                    var property = dop.getObjectProperty(item);
-                    // if ( Number(property) !== Number(index) )
-                        // debugger;
-                    t.equal(Number(property), Number(index), 'correct path for subobject: '+index + ', Case:');
-                }
-                else if (item.constructor === Object)
-                    t.equal(false, true, 'object not registered: '+index + ', Case:' + ' ' + gify(item));
-            }
-        }
-    });
-
-
-    var objectRegistered = dop.register({array:array.slice(0)});
-    arrayOriginal = objectRegistered.array;
-    arrayOriginal[13] = true;
-    copy = arrayOriginal.slice(0);
-    var output1 = arrayOriginal.sort();
-    var output2 = copy.sort();
-    t.equal(gify(output1), gify(output2), 'output');
-
-
-    t.end();
-});
-
-
-
-
-
-var array = [
-    [4,5,6,7],
-    'string',
-    true,
-    -123,
-    {},
-    // NaN, // not working with deepEqual, cuz (NaN===NaN) -> false
-    -Infinity,
-    {},
-    1.234153454354341,
-    12313214234312324353454534534,
-    {},
-    undefined,
-    null,
-    {subobject:'value'},
-    Symbol('sym'),
-    {},
-    new Date(),
-    new gify(),
-    /myregexp/g
-];
-
-var types = [0, 1, Infinity, -Infinity, 2, 12, 50, -1, -2, -12, -50, null, undefined, '', '0', '2', '50'];
-var argsCases = [[]];
-var single = types.map(function(item) {return [item]});
-argsCases = argsCases.concat(single) 
-var cmb = Combinatorics.baseN(types, 2);
-while(a = cmb.next()) argsCases.push(a)//argsCases.push(a, a.concat({},'string',{},1,null,undefined,new Date(),/test/,new gify(),true,{},[{}]));
-
