diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..dddb866
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,7 @@
+{
+    "printWidth": 80,
+    "tabWidth": 4,
+    "semi": false,
+    "singleQuote": true,
+    "bracketSpacing": true
+}
diff --git a/README.md b/README.md
index 37ba75a..bea6e63 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@
 
 [![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)
+<!--[![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)-->
 
 <br/>
 <a href="https://distributedobjectprotocol.org/guide/javascript">https://distributedobjectprotocol.org/</a>
@@ -59,7 +59,9 @@ const store = createStore({ players: 0 })
 
 function subscribeToServerStore(listener) {
     // Incrementing number of player as a patch
-    const listeners = store.applyPatch({ players: store.state.players + 1 })
+    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
diff --git a/package.json b/package.json
index 7735917..2d50e8d 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,9 @@
 {
     "name": "dop",
-    "version": "1.1.0",
+    "version": "1.5.4",
     "main": "dist/dop.js",
     "browser": "dist/dop.umd.js",
+    "module": "src/index.js",
     "license": "MIT",
     "url": "https://distributedobjectprotocol.org/",
     "repository": {
@@ -49,7 +50,8 @@
         "testing": "npm test -- --watch"
     },
     "files": [
-        "dist"
+        "dist",
+        "src"
     ],
     "ava": {
         "files": [
@@ -58,12 +60,5 @@
         "require": [
             "esm"
         ]
-    },
-    "prettier": {
-        "printWidth": 80,
-        "tabWidth": 4,
-        "semi": false,
-        "singleQuote": true,
-        "bracketSpacing": true
     }
 }
diff --git a/rollup.config.js b/rollup.config.js
index 3af1c81..193e9f9 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -6,38 +6,47 @@ import json from '@rollup/plugin-json'
 import dop from './src/index'
 
 export default [
-    // // module/
-    // {
-    //     input: 'src/index',
-    //     external: ['ms'],
-    //     preserveModules: true,
-    //     output: [{ dir: './module', format: 'es' }],
-    //     plugins: [json(), buble()]
-    // },
-
     // dop.js
     {
         input: 'src/index',
         external: ['ms'],
         output: [
-            { file: pkg.main, format: 'cjs' }
             // { file: pkg.module, format: 'es' }
+            {
+                file: pkg.main,
+                format: 'cjs',
+                exports: 'named',
+            },
         ],
-        plugins: [json(), buble()]
+        plugins: [json(), buble()],
     },
 
     // dop.umd.js
     {
         input: 'src/index',
-        output: { name: 'dop', file: pkg.browser, format: 'umd' },
+        output: {
+            name: 'dop',
+            file: pkg.browser,
+            format: 'umd',
+            exports: 'named',
+        },
         plugins: [
             json(),
             buble(),
             uglify({
                 mangle: {
-                    reserved: Object.keys(dop.TYPE)
-                }
-            })
-        ]
-    }
+                    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/applyPatchFactory.js b/src/api/applyPatchFactory.js
index 3751d4d..c8d53da 100644
--- a/src/api/applyPatchFactory.js
+++ b/src/api/applyPatchFactory.js
@@ -1,69 +1,84 @@
-import { isPlainObject } from '../util/is'
-import { setDeep } from '../util/set'
-import forEachObject from '../util/forEachObject'
+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) {
-    return function applyPatch(target, patch) {
+    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 = { '': {} }
 
-        forEachObject(
-            patch_root,
-            target_root,
-            ({ origin, destiny, prop, path }) => {
-                const origin_value = origin[prop]
-                const destiny_value = destiny[prop]
-                const had_prop = destiny.hasOwnProperty(prop)
-                if (
-                    !had_prop ||
-                    (origin_value !== destiny_value &&
-                        !(
-                            isPlainObject(origin_value) &&
-                            isPlainObject(destiny_value)
-                        ))
-                ) {
-                    // This is where the merge with plain objects happen
-                    destiny[prop] = isPlainObject(origin_value)
-                        ? applyPatch({}, origin_value).result
-                        : origin_value
+        function addMutation(target, prop, old_value, path) {
+            mutations.push({
+                target,
+                prop,
+                old_value,
+                path,
+            })
+        }
 
-                    // Applying patches
-                    const old_value = patchers.reduce(
-                        (old_value, p) =>
-                            p({
-                                origin,
-                                destiny,
-                                prop,
-                                path,
-                                old_value,
-                                had_prop,
-                                applyPatch
-                            }),
-                        destiny_value
-                    )
+        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
 
-                    // We register the mutation if old_value is different to the new value
-                    if (destiny[prop] !== old_value) {
-                        setDeep(unpatch_root, path.slice(0), old_value)
-                        mutations.push({
-                            old_value,
-                            object: destiny,
+                // Applying patches
+                const old_value = patchers.reduce(
+                    (old_value, patcher) =>
+                        patcher({
+                            patch,
+                            target,
                             prop,
-                            path: path.slice(1)
-                        })
-                    }
+                            old_value,
+                            applyPatch,
+                        }),
+                    target_value
+                )
 
-                    return false // we don't go deeper
+                // 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
+            mutations,
         }
     }
+
+    return applyPatch
 }
diff --git a/src/api/createNodeFactory.js b/src/api/createNodeFactory.js
index 4e423bb..430f296 100644
--- a/src/api/createNodeFactory.js
+++ b/src/api/createNodeFactory.js
@@ -1,50 +1,40 @@
-import { isFunction, isInteger, isArray } from '../util/is'
-import createRequest from '../util/createRequest'
-import localProcedureCall from '../util/localProcedureCall'
+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,
-        max_remote_functions = Infinity
+        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 = {}
-        const local_functions_id = {}
-        const local_functions = new Map()
-        const remote_functions_id = {}
-        const remote_functions = new Set()
-        let local_function_index = 0
-        let remote_function_index = 0
         let request_id_index = 0
 
-        // encode / decode params
         const encode_params = {
-            remote_functions,
-            local_functions,
-            registerLocalFunctionFromEncode
-        }
-        const decode_params = {
-            remote_functions_id,
-            createRemoteFunction
+            local_rpcs,
+            registerLocalRpcFromEncode,
         }
 
-        function registerLocalFunction(function_id, fn) {
-            local_functions_id[function_id] = fn
-            local_functions.set(fn, function_id)
+        function registerRpc(function_id, fn) {
+            local_rpcs_id[function_id] = fn
+            local_rpcs.set(fn, function_id)
             return function_id
         }
 
-        function createRemoteFunction(function_id) {
-            if (remote_functions.size / 2 >= max_remote_functions) {
-                return null
-            }
+        function createRpc(function_id) {
             const makeCall = (request_id, args) => {
                 const data = [request_id, function_id]
                 if (args.length > 0) data.push(args)
-                api.send(encode(data, encode_params))
+                node.send(encode(data, encode_params))
                 return data
             }
-            const fn = (...args) => {
+
+            const rpc = (...args) => {
                 const request_id = ++request_id_index
                 const req = createRequest()
                 const { resolve, reject } = req
@@ -54,95 +44,129 @@ export default function createNodeFactory({ encode, decode }) {
                     return req
                 }
                 req.data = makeCall(request_id, args)
-                req.node = api
+                req.node = node
                 req.createdAt = new Date().getTime()
-                req.resolve = value => resolveOrReject(resolve, value)
-                req.reject = error => resolveOrReject(reject, error)
+                req.resolve = (value) => resolveOrReject(resolve, value)
+                req.reject = (error) => resolveOrReject(reject, error)
                 requests[request_id] = req
                 return req
             }
-            fn.stub = (...args) => {
+
+            rpc.push = (...args) => {
                 makeCall(0, args)
             }
 
-            remote_functions.add(fn)
-            remote_functions.add(fn.stub)
-            remote_functions_id[function_id] = fn
-            return fn
+            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 remote_function_id = remote_function_index++
-            const local_function_id = local_function_index++
-            api.send = msg => send(serialize(msg))
-            if (isFunction(fn)) registerLocalFunction(local_function_id, fn)
-            return createRemoteFunction(remote_function_id)
+            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)) return false
-            msg = decode(msg, decode_params)
+            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 [id, function_id, args] = msg
+            let args = msg[2]
             const response_id = -id
 
-            if (isInteger(id)) {
-                const fn = local_functions_id[function_id]
-
-                if (id > -1 && isFunction(fn)) {
-                    args = isArray(msg[2]) ? msg[2] : []
-
-                    // Request without response
-                    if (id === 0) {
-                        const req = { node: api }
-                        args.push(req)
-                        fn.apply(req, args)
-                    }
-
-                    // Request
-                    else {
-                        const req = createRequest()
-                        const response = [response_id]
-                        req.node = api
-                        req.then(value => {
-                            response.push(0) // no errors
-                            if (value !== undefined) response.push(value)
-                            api.send(encode(response, encode_params))
-                        }).catch(error => {
-                            response.push(error === 0 ? null : error)
-                            api.send(encode(response, encode_params))
-                        })
-                        args.push(req)
-                        localProcedureCall(fn, req, args)
-                    }
-                    return true
+            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)
                 }
 
-                // 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
+                // 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 registerLocalFunctionFromEncode(fn) {
-            return registerLocalFunction(local_function_index++, fn)
+        function registerLocalRpcFromEncode(fn) {
+            return registerRpc(getNextLocalFunctionId(), fn)
         }
 
-        const api = {
+        const node = {
             open,
             message,
             requests,
-            remote_functions // Exposing this can be used to know if a function is a remote function
+            registerRpc,
+            createRpc,
         }
 
-        return api
+        return node
     }
 }
diff --git a/src/api/createStoreFactory.js b/src/api/createStoreFactory.js
index 1f8da30..a820a06 100644
--- a/src/api/createStoreFactory.js
+++ b/src/api/createStoreFactory.js
@@ -1,5 +1,5 @@
-import { createPatchFromMutations } from '../util/set'
-import { isFunction } from '../util/is'
+import { createPatchAndUnpatchFromMutations } from '../util/patches.js'
+import { isFunction } from '../util/is.js'
 
 export default function createStoreFactory(applyPatchFunction) {
     return function createStore(state) {
@@ -14,31 +14,33 @@ export default function createStoreFactory(applyPatchFunction) {
             listeners.delete(listener)
         }
 
-        function applyPatch(patch) {
-            const { mutations, result, unpatch } = applyPatchFunction(
-                api.state,
-                patch
-            )
-            api.state = result
+        function applyPatch(patch_or_fn) {
+            const applied = applyPatchFunction(api.state, patch_or_fn)
 
             const outputs = Array.from(listeners.entries()).map(
                 ([listener, filter]) => {
-                    const mts = isFunction(filter)
-                        ? mutations.filter(filter)
-                        : mutations.slice(0)
+                    const mutations = isFunction(filter)
+                        ? applied.mutations.filter(filter)
+                        : applied.mutations.slice(0)
+
+                    const { patch, unpatch } =
+                        createPatchAndUnpatchFromMutations(
+                            mutations,
+                            applied.patch
+                        )
 
-                    const { patch, unpatch } = createPatchFromMutations(mts)
                     return {
                         listener,
                         patch,
                         unpatch,
-                        mutations: mts
+                        mutations,
                     }
                 }
             )
 
-            outputs.mutations = mutations
-            outputs.unpatch = unpatch
+            outputs.mutations = applied.mutations
+            outputs.patch = applied.patch
+            outputs.unpatch = applied.unpatch
 
             return outputs
         }
@@ -48,7 +50,7 @@ export default function createStoreFactory(applyPatchFunction) {
             listeners,
             subscribe,
             unsubscribe,
-            applyPatch
+            applyPatch,
         }
 
         return api
diff --git a/src/const.js b/src/const.js
index 3008bc1..e60add0 100644
--- a/src/const.js
+++ b/src/const.js
@@ -1,6 +1,13 @@
+export const ESCAPE_KEY = '$escape'
+export const RPC_KEY = '$r'
 export const DELETE_KEY = '$d'
-export const FUNCTION_KEY = '$f'
-export const REPLACE_KEY = '$r'
+export const REPLACE_KEY = '$e'
 export const SPLICE_KEY = '$s'
-export const INNER_KEY = '$i'
-export const ESCAPE_KEY = '$escape'
+export const SWAP_KEY = '$w'
+export const MULTI_KEY = '$m'
+
+export const RPC_CREATOR = {
+    ENTRY: 'ENTRY',
+    REQUEST: 'REQUEST',
+    RESPONSE: 'RESPONSE',
+}
diff --git a/src/index.js b/src/index.js
index 2c2c725..c14d793 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,17 +1,22 @@
-import { version } from '../package.json'
-import merge from './util/merge'
-import converter from './util/converter'
-import { isFunction } from './util/is'
-import createNodeFactory from './api/createNodeFactory'
-import createStoreFactory from './api/createStoreFactory'
-import applyPatchFactory from './api/applyPatchFactory'
-
-import Function from './types/Function'
-import Array from './types/Array'
-import Delete from './types/Delete'
-import Replace from './types/Replace'
-import Splice from './types/Splice'
-import Inner from './types/Inner'
+// 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 = []
@@ -30,12 +35,13 @@ function factory() {
         if (isFunction(decode)) decoders.push(decode)
     }
 
-    addType(Function)
-    addType(Array)
+    addType(Primitives)
+    addType(Rpc)
     addType(Delete)
     addType(Replace)
     addType(Splice)
-    addType(Inner)
+    addType(Swap)
+    addType(Multi)
 
     return {
         version,
@@ -43,6 +49,7 @@ function factory() {
         merge,
         encode,
         decode,
+        producePatch,
         applyPatch,
         createNode,
         createStore,
@@ -51,9 +58,27 @@ function factory() {
             Delete,
             Replace,
             Splice,
-            Inner
-        }
+            Swap,
+            Multi,
+        },
     }
 }
 
-export default factory()
+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/types/Array.js b/src/types/Array.js
deleted file mode 100644
index cd0e2a0..0000000
--- a/src/types/Array.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { isArray } from '../util/is'
-import merge from '../util/merge'
-
-export default function Array() {}
-
-Array.patch = function({ origin, destiny, prop, old_value }) {
-    const origin_value = origin[prop]
-    if (isArray(origin_value)) {
-        destiny[prop] = merge([], origin_value)
-    }
-    return old_value
-}
diff --git a/src/types/Delete.js b/src/types/Delete.js
index 4dfca46..ea87b83 100644
--- a/src/types/Delete.js
+++ b/src/types/Delete.js
@@ -1,6 +1,6 @@
-import { ESCAPE_KEY, DELETE_KEY } from '../const'
-import { getUniqueKey } from '../util/get'
-import { isValidToEscape } from '../util/isValid'
+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)) {
@@ -8,26 +8,23 @@ export default function Delete() {
     }
 }
 
-Delete.patch = function({ origin, destiny, prop, old_value, had_prop }) {
-    if (origin[prop] instanceof Delete || origin[prop] === Delete) {
-        delete destiny[prop]
-    }
-    if (!had_prop) {
-        old_value = 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 }) {
+Delete.encode = function ({ value }) {
     if (value instanceof Delete || value === Delete) {
-        return { [DELETE_KEY]: 0 } // we don't go deeper
+        return { [DELETE_KEY]: 0 }
     } else if (isValidToDecodeDelete({ value })) {
-        return { [ESCAPE_KEY]: value } // we don't go deeper
+        return { [ESCAPE_KEY]: value }
     }
     return value
 }
 
-Delete.decode = function({ value }) {
+Delete.decode = function ({ value }) {
     if (isValidToDecodeDelete({ value })) {
         return new Delete()
     } else if (
diff --git a/src/types/Function.js b/src/types/Function.js
deleted file mode 100644
index eda48df..0000000
--- a/src/types/Function.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import { ESCAPE_KEY, FUNCTION_KEY } from '../const'
-import { getUniqueKey } from '../util/get'
-import { isInteger, isFunction } from '../util/is'
-import { isValidToDecode, isValidToEscape } from '../util/isValid'
-
-export default function Function() {}
-
-Function.encode = function({
-    value,
-    remote_functions,
-    local_functions,
-    registerLocalFunctionFromEncode
-}) {
-    if (isFunction(value)) {
-        if (remote_functions.has(value)) return null
-        const function_id = local_functions.has(value)
-            ? local_functions.get(value)
-            : registerLocalFunctionFromEncode(value)
-        return { [FUNCTION_KEY]: function_id }
-    } else if (isValidToDecode({ value, key: FUNCTION_KEY })) {
-        return { [ESCAPE_KEY]: value } // we don't go deeper
-    }
-    return value
-}
-
-Function.decode = function({
-    value,
-    remote_functions_id,
-    createRemoteFunction
-}) {
-    if (
-        getUniqueKey(value) === FUNCTION_KEY &&
-        isInteger(value[FUNCTION_KEY])
-    ) {
-        const function_id = value[FUNCTION_KEY]
-        const fn = remote_functions_id[function_id]
-        return isFunction(fn) ? fn : createRemoteFunction(function_id)
-    } else if (
-        isValidToEscape({ value }) &&
-        isValidToDecode({ value: value[ESCAPE_KEY], key: FUNCTION_KEY })
-    ) {
-        return value[ESCAPE_KEY]
-    }
-    return value
-}
diff --git a/src/types/Inner.js b/src/types/Inner.js
deleted file mode 100644
index 845f375..0000000
--- a/src/types/Inner.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import { ESCAPE_KEY, INNER_KEY } from '../const'
-import { isValidToEscape } from '../util/isValid'
-import { isArray, isPlainObject } from '../util/is'
-import { getUniqueKey } from '../util/get'
-import Delete from './Delete'
-
-export default function Inner(patch) {
-    if (!isPlainObject(patch))
-        throw 'TYPE.Inner only accepts plain objects as argument'
-    if (!(this instanceof Inner)) {
-        return new Inner(patch)
-    }
-    this.patch = patch
-}
-
-Inner.patch = function({ origin, destiny, prop, old_value, applyPatch }) {
-    const origin_value = origin[prop]
-    if (origin_value instanceof Inner) {
-        destiny[prop] = old_value
-        if (isArray(old_value)) {
-            const patches = origin_value.patch
-            const unpatches = {}
-            const length = old_value.length
-            for (const key in patches) {
-                const had_prop = old_value.hasOwnProperty(key)
-                const patched = applyPatch(old_value[key], patches[key])
-                if (
-                    old_value[key] !== patched.result ||
-                    isPlainObject(patches[key])
-                ) {
-                    old_value[key] = patched.result
-                    unpatches[key] = had_prop ? patched.unpatch : Delete()
-                }
-            }
-            if (old_value.length !== length) {
-                unpatches.length = length
-            }
-            // console.log({ result: old_value, unpatches })
-            return new Inner(unpatches)
-        }
-    }
-    return old_value
-}
-
-Inner.encode = function({ value, encode }) {
-    if (value instanceof Inner) {
-        return { [INNER_KEY]: encode(value.patch) }
-    } else if (isValidToDecodeInner({ value })) {
-        return { [ESCAPE_KEY]: value }
-    }
-    return value
-}
-
-Inner.decode = function({ value, decode }) {
-    if (isValidToDecodeInner({ value })) {
-        return new Inner(decode(value[INNER_KEY]))
-    } else if (
-        isValidToEscape({ value }) &&
-        isValidToDecodeInner({ value: value[ESCAPE_KEY] })
-    ) {
-        return value[ESCAPE_KEY]
-    }
-    return value
-}
-
-function isValidToDecodeInner({ value }) {
-    return getUniqueKey(value) === INNER_KEY && isPlainObject(value[INNER_KEY])
-}
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
index d94b754..f8367b8 100644
--- a/src/types/Replace.js
+++ b/src/types/Replace.js
@@ -1,5 +1,6 @@
-import { ESCAPE_KEY, REPLACE_KEY } from '../const'
-import { isValidToDecode, isValidToEscape } from '../util/isValid'
+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)) {
@@ -8,26 +9,26 @@ export default function Replace(value) {
     this.value = value
 }
 
-Replace.patch = function({ origin, destiny, prop, old_value }) {
-    if (origin[prop] instanceof Replace) {
-        destiny[prop] = origin[prop].value
-        return new Replace(old_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 }) {
+Replace.encode = function ({ value, encode }) {
     if (value instanceof Replace) {
-        return { [REPLACE_KEY]: value.value } // we don't go deeper
+        return { [REPLACE_KEY]: encode(value.value) }
     } else if (isValidToDecode({ value, key: REPLACE_KEY })) {
-        return { [ESCAPE_KEY]: value } // we don't go deeper
+        return { [ESCAPE_KEY]: value }
     }
     return value
 }
 
-Replace.decode = function({ value }) {
+Replace.decode = function ({ value, decode }) {
     if (isValidToDecode({ value, key: REPLACE_KEY })) {
-        return new Replace(value[REPLACE_KEY])
+        return new Replace(decode(value[REPLACE_KEY]))
     } else if (
         isValidToEscape({ value }) &&
         isValidToDecode({ value: value[ESCAPE_KEY], key: REPLACE_KEY })
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
index 60a2d61..7e056da 100644
--- a/src/types/Splice.js
+++ b/src/types/Splice.js
@@ -1,7 +1,6 @@
-import { isArray } from '../util/is'
-import { isValidToEscape } from '../util/isValid'
-import { ESCAPE_KEY, SPLICE_KEY } from '../const'
-import { getUniqueKey } from '../util/get'
+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)) {
@@ -10,12 +9,12 @@ export default function Splice(...args) {
     this.args = args
 }
 
-Splice.patch = function({ origin, destiny, prop, old_value }) {
-    const origin_value = origin[prop]
-    if (origin_value instanceof Splice) {
-        destiny[prop] = old_value
+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 } = origin_value
+            const { args } = patch_value
             if (args[0] < 0) {
                 args[0] = old_value.length + args[0]
             }
@@ -27,18 +26,18 @@ Splice.patch = function({ origin, destiny, prop, old_value }) {
     return old_value
 }
 
-Splice.encode = function({ value }) {
+Splice.encode = function ({ value, encode }) {
     if (value instanceof Splice) {
-        return { [SPLICE_KEY]: value.args }
+        return { [SPLICE_KEY]: encode(value.args) }
     } else if (isValidToDecode({ value, key: SPLICE_KEY })) {
         return { [ESCAPE_KEY]: value }
     }
     return value
 }
 
-Splice.decode = function({ value }) {
+Splice.decode = function ({ value, decode }) {
     if (isValidToDecode({ value, key: SPLICE_KEY })) {
-        return Splice.apply(null, value[SPLICE_KEY])
+        return Splice.apply(null, decode(value[SPLICE_KEY]))
     } else if (
         isValidToEscape({ value }) &&
         isValidToDecode({ value: value[ESCAPE_KEY], key: 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/util/converter.js b/src/util/converter.js
deleted file mode 100644
index f6cfc5f..0000000
--- a/src/util/converter.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import forEachObject from '../util/forEachObject'
-import merge, { mergeMutator } from '../util/merge'
-import { isArray } from '../util/is'
-
-export default function converter(origin, params, converters) {
-    const destiny = isArray(origin) ? [] : {}
-    forEachObject(origin, destiny, ({ origin, prop, destiny, path }) => {
-        const value = converters.reduce(
-            (value, converter) =>
-                converter(
-                    merge(
-                        {
-                            value,
-                            origin,
-                            destiny,
-                            prop,
-                            path
-                        },
-                        params
-                    )
-                ),
-            origin[prop]
-        )
-        if (origin[prop] !== value) {
-            destiny[prop] = value
-            return false // we don't go deeper
-        } else {
-            return mergeMutator({ origin, destiny, prop })
-        }
-    })
-    return destiny
-}
diff --git a/src/util/createRequest.js b/src/util/createRequest.js
deleted file mode 100644
index b2f2faa..0000000
--- a/src/util/createRequest.js
+++ /dev/null
@@ -1,29 +0,0 @@
-export default 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/forEach.js b/src/util/forEach.js
index a9bb44d..34c667a 100644
--- a/src/util/forEach.js
+++ b/src/util/forEach.js
@@ -1,7 +1,7 @@
-import { isArray } from './is'
+import { isArray, isObject } from './is.js'
 
 // https://stackoverflow.com/questions/27433075/using-a-for-each-loop-on-an-empty-array-in-javascript
-export default function forEach(object, callback) {
+export function forEach(object, callback) {
     if (isArray(object)) {
         for (let prop = 0; prop < object.length; ++prop) {
             callback(object[prop], prop)
@@ -12,3 +12,16 @@ export default function forEach(object, callback) {
         }
     }
 }
+
+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/forEachObject.js b/src/util/forEachObject.js
deleted file mode 100644
index adf3d3f..0000000
--- a/src/util/forEachObject.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { isObject } from './is'
-import forEach from './forEach'
-
-// https://jsperf.com/dop-foreachobject
-// https://2ality.com/2019/10/shared-mutable-state.html
-export default function forEachObject(origin, destiny, mutator) {
-    forEachObjectLoop(origin, destiny, mutator, [])
-}
-
-function forEachObjectLoop(origin, destiny, mutator, path) {
-    forEach(origin, (value_origin, prop) => {
-        path.push(prop)
-        const shallWeGoDown = mutator({ origin, destiny, prop, path })
-        if (shallWeGoDown !== false && isObject(value_origin))
-            forEachObjectLoop(value_origin, destiny[prop], mutator, path)
-        path.pop()
-    })
-}
diff --git a/src/util/get.js b/src/util/get.js
deleted file mode 100644
index 981adec..0000000
--- a/src/util/get.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { is, isPlain } from './is'
-import merge from './merge'
-
-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
-}
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/is.js b/src/util/is.js
index 98a170c..312c1f7 100644
--- a/src/util/is.js
+++ b/src/util/is.js
@@ -1,3 +1,6 @@
+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'
@@ -38,3 +41,23 @@ export function isInteger(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/isValid.js b/src/util/isValid.js
deleted file mode 100644
index a0c27cb..0000000
--- a/src/util/isValid.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { getUniqueKey } from '../util/get'
-import { ESCAPE_KEY } from '../const'
-
-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/localProcedureCall.js b/src/util/localProcedureCall.js
deleted file mode 100644
index 2b09b9a..0000000
--- a/src/util/localProcedureCall.js
+++ /dev/null
@@ -1,14 +0,0 @@
-export default function localProcedureCall(fn, req, args) {
-    try {
-        const output = fn.apply(req, args)
-        if (output !== req) {
-            output instanceof Promise
-                ? output.then(req.resolve).catch(req.reject)
-                : req.resolve(output)
-        }
-    } catch (error) {
-        // https://airbrake.io/blog/nodejs-error-handling/nodejs-error-class-hierarchy
-        if (error instanceof Error) throw error
-        req.reject(error)
-    }
-}
diff --git a/src/util/merge.js b/src/util/merge.js
index 1dd2644..db6c32b 100644
--- a/src/util/merge.js
+++ b/src/util/merge.js
@@ -1,89 +1,74 @@
-import { is, isPlain } from './is'
-import forEachObject from './forEachObject'
+import { is, isPlain, isArray } from './is.js'
+import { forEachDeep } from './forEach.js'
+import { getDeep } from './getset.js'
 
 // https://jsperf.com/merge-challenge
-export default function merge(destiny, origin) {
+export function merge(target, patch) {
     const args = arguments
     if (args.length > 2) {
-        // Remove the destiny 2 arguments of the arguments and add thoose arguments as merged at the begining
-        Array.prototype.splice.call(
-            args,
-            0,
-            2,
-            merge.call(this, destiny, origin)
-        )
+        // 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 (origin === destiny) {
-        return destiny
+    } else if (patch === target) {
+        return target
     } else {
-        forEachObject(origin, destiny, mergeMutator)
-        return destiny
+        mergeCore(patch, target, mergeMutator)
+        return target
     }
 }
 
-export function mergeMutator({ origin, destiny, prop }) {
-    const origin_value = origin[prop]
-    const destiny_value = destiny[prop]
+// 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 (!destiny.hasOwnProperty(prop) || tof_origin != tof_destiny) {
-            destiny[prop] = tof_origin == 'array' ? [] : {}
+        if (!target.hasOwnProperty(prop) || tof_origin != tof_destiny) {
+            target[prop] = tof_origin == 'array' ? [] : {}
         }
     } else {
-        destiny[prop] = origin_value
+        target[prop] = origin_value
         return false // we dont go deeper
     }
 }
 
-// export default createCustomMerge('merge', ({ origin, destiny, prop }) => {
-//     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)) {
-//         if (!destiny.hasOwnProperty(prop) || tof_origin !== tof_destiny) {
-//             destiny[prop] = tof_origin === 'array' ? [] : {}
-//             if (tof_origin === 'array') {
-//                 const array = []
-//                 if (tof_destiny === 'object') {
-//                     Object.keys(destiny_value)
-//                         .filter(key => !isNaN(Number(key)))
-//                         .forEach(key => (array[key] = destiny_value[key]))
-//                 }
-//                 destiny[prop] = array
-//             } else {
-//                 destiny[prop] = {}
-//             }
-//         }
-//     } else if (tof_origin === 'undefined' && destiny.hasOwnProperty(prop)) {
-//         //skipping
-//     } else {
-//         destiny[prop] = origin_value
-//         return true // skipping
-//     }
-// })
-
-// // https://stackoverflow.com/questions/5905492/dynamic-function-name-in-javascript
-// export default function createCustomMerge(function_name, mutator) {
-//     return createFunction(function_name, function recursive(destiny, origin) {
-//         const args = arguments
-//         if (args.length > 2) {
-//             // Remove the destiny 2 arguments of the arguments and add thoose arguments as recursived at the begining
-//             Array.prototype.splice.call(
-//                 args,
-//                 0,
-//                 2,
-//                 recursive.call(this, destiny, origin)
-//             )
-//             // Recursion
-//             return recursive.apply(this, args)
-//         } else if (origin === destiny) {
-//             return destiny
-//         } else {
-//             forEachObject(origin, mutator, destiny)
-//             return destiny
-//         }
-//     })
-// }
+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/set.js b/src/util/set.js
deleted file mode 100644
index 2cfcb40..0000000
--- a/src/util/set.js
+++ /dev/null
@@ -1,21 +0,0 @@
-export function setDeep(unpatch, path, value) {
-    const prop = path.shift()
-    if (path.length === 0) {
-        unpatch[prop] = value
-        return
-    }
-    if (!unpatch.hasOwnProperty(prop)) {
-        unpatch[prop] = {}
-    }
-    return setDeep(unpatch[prop], path, value)
-}
-
-export function createPatchFromMutations(mutations) {
-    const patch = {}
-    const unpatch = {}
-    mutations.forEach(({ object, prop, path, old_value }) => {
-        setDeep(patch, path.slice(0), object[prop])
-        setDeep(unpatch, path.slice(0), old_value)
-    })
-    return { patch, unpatch }
-}
diff --git a/test/.addType.js b/test/.addType.js
index 2e0b882..90e8060 100644
--- a/test/.addType.js
+++ b/test/.addType.js
@@ -1,7 +1,7 @@
 import test from 'ava'
-import { applyPatch, encode, decode, addType } from '../'
+import { applyPatch, encode, decode, addType } from '../src'
 import Splice from '../src/types/Splice'
-import { getNewPlain } from '../src/util/get'
+import { getNewPlain } from '../src/util/getset'
 import { isPlainObject } from '../src/util/is'
 
 // Defining a type that push elements to an array
@@ -11,21 +11,21 @@ function Push(...elements) {
     }
     this.elements = elements
 }
-Push.patch = function({ origin, destiny, prop, old_value }) {
-    const originValue = origin[prop]
+Push.patch = function ({ patch, target, prop, old_value }) {
+    const originValue = patch[prop]
     if (isArray(old_value) && originValue instanceof Push) {
-        destiny[prop] = old_value
+        target[prop] = old_value
         return Push.apply(null, originValue.elements)
     }
     return old_value
 }
-Push.encode = function({ value }) {
+Push.encode = function ({ value }) {
     if (value instanceof Push) {
         return { $push: value.elements }
     }
     return value
 }
-Push.decode = function({ value }) {
+Push.decode = function ({ value }) {
     if (getUniqueKey(value) === '$push' && isArray(value['$push'])) {
         return Push.apply(null, value['$push'])
     }
diff --git a/test/api.js b/test/api.js
index ee0b816..e4afb3d 100644
--- a/test/api.js
+++ b/test/api.js
@@ -1,37 +1,39 @@
 import test from 'ava'
-import dop from '../'
-import { createNode } from '../'
+import dop from '../src'
+import { createNode } from '../src'
 
-const exported = [
-    'version',
-    'factory',
-    'merge',
-    'encode',
-    'decode',
-    'applyPatch',
-    'createNode',
-    'createStore',
-    'addType',
-    'TYPE'
-]
+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) {
-    t.deepEqual(Object.keys(dop), exported)
+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) {
+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('isRemoteFunction', async t => {
-    const node = createNode()
-    const { remote_functions } = node
-    const isRemoteFunction = f => remote_functions.has(f)
-    const callClient = node.open()
-    t.true(isRemoteFunction(callClient))
-    t.false(isRemoteFunction(() => {}))
-    t.false(isRemoteFunction('other'))
+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/applyPatch.js b/test/applyPatch.js
deleted file mode 100644
index e38fdb4..0000000
--- a/test/applyPatch.js
+++ /dev/null
@@ -1,570 +0,0 @@
-import test from 'ava'
-import { applyPatch, TYPE } from '../'
-import { testPatchUnpatch } from './utils'
-
-test('basic mutation', function(t) {
-    const target = { number: 1 }
-    const patch = { number: 2 }
-    const expected = { number: 2 }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-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 }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('value didnt exists', function(t) {
-    const target = {}
-    const patch = { value: true }
-    const expected = { value: true }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('deletion', function(t) {
-    const target = { value: 12345 }
-    const patch = { value: TYPE.Delete() }
-    const expected = {}
-
-    testPatchUnpatch(t, target, patch, 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 }
-    }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('from deep to other', function(t) {
-    const target = { value: { more: false } }
-    const patch = { value: true }
-    const expected = { value: true }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('deep value', function(t) {
-    const target = { value: { more: false } }
-    const patch = { value: { more: true } }
-    const expected = { value: { more: true } }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-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 }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('mutating multiple levels not defineds', function(t) {
-    const target = {}
-    const patch = { value: { more: true }, other: true }
-    const expected = { value: { more: true }, other: true }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-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 = {}
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('function to target', function(t) {
-    const target = { prop: () => {} }
-    const patch = { prop: {} }
-    const expected = { prop: {} }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('target to function', function(t) {
-    const f = () => {}
-    const target = { prop: {} }
-    const patch = { prop: f }
-    const expected = { prop: f }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-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 { mutations } = testPatchUnpatch(t, target, patch, expected)
-    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 }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('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('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('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.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] }
-
-    testPatchUnpatch(t, target, patch, expected, false)
-    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' }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('from target to array', function(t) {
-    const target = { objarr: { value: true } }
-    const patch = { objarr: [0, 1] }
-    const expected = { objarr: [0, 1] }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('from array to target', function(t) {
-    const target = { objarr: [0, 1] }
-    const patch = { objarr: { value: true } }
-    const expected = { objarr: { value: true } }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('should assign `null` values', function(t) {
-    const target = { value: false }
-    const patch = { value: null }
-    const expected = { value: null }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('should assign `undefined` values', function(t) {
-    const target = { value: false }
-    const patch = { value: undefined, value2: undefined }
-    const expected = { value: undefined, value2: undefined }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-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 }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('same patch as target generate no mutations', function(t) {
-    const target = { value: false }
-    const expected = { value: false }
-
-    const { mutations, unpatch } = testPatchUnpatch(t, target, target, 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)
-    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 { mutations, unpatch } = testPatchUnpatch(t, target, patch, expected)
-    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)
-    t.is(mutations.length, 30)
-})
-
-test('1 / https://tools.ietf.org/html/rfc7386 ', function(t) {
-    const target = { a: 'b' }
-    const patch = { a: 'c' }
-    const expected = { a: 'c' }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('2 / https://tools.ietf.org/html/rfc7386 ', function(t) {
-    const target = { a: 'b' }
-    const patch = { b: 'c' }
-    const expected = { a: 'b', b: 'c' }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('3 / https://tools.ietf.org/html/rfc7386 ', function(t) {
-    const target = { a: 'b' }
-    const patch = { a: TYPE.Delete() }
-    const expected = {}
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-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' }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('5 / https://tools.ietf.org/html/rfc7386 ', function(t) {
-    const target = { a: ['b'] }
-    const patch = { a: 'c' }
-    const expected = { a: 'c' }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('6 / https://tools.ietf.org/html/rfc7386 ', function(t) {
-    const target = { a: 'c' }
-    const patch = { a: ['b'] }
-    const expected = { a: ['b'] }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-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' } }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('8 / https://tools.ietf.org/html/rfc7386 ', function(t) {
-    const target = { a: { b: 'c' } }
-    const patch = { a: [1] }
-    const expected = { a: [1] }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-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)
-})
-
-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 }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('13/b', function(t) {
-    const del = TYPE.Delete()
-    const target = { e: del }
-    const patch = { a: 1 }
-    const expected = { e: del, a: 1 }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
-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 }
-
-    // not sure about this case because the unpatch would result in {}
-    testPatchUnpatch(t, target, patch, expected, false)
-})
-
-test('14 / https://tools.ietf.org/html/rfc7386 ', function(t) {
-    const target = [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: {} } }
-
-    testPatchUnpatch(t, target, patch, expected)
-})
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/createNode.js b/test/create_node.js
similarity index 56%
rename from test/createNode.js
rename to test/create_node.js
index c8df6bb..90759b5 100644
--- a/test/createNode.js
+++ b/test/create_node.js
@@ -1,7 +1,8 @@
 import test from 'ava'
-import { createNode } from '../'
+import { createNode } from '../src'
+import { RPC_CREATOR } from '../src/const'
 
-test('Api', async t => {
+test('Api', async (t) => {
     const server = createNode()
     const client = createNode()
 
@@ -17,7 +18,7 @@ test('Api', async t => {
     const callClient = server.open(client.message)
     const promise = callClient(2, 5)
 
-    t.deepEqual(Object.keys(server).length, 5)
+    t.deepEqual(Object.keys(server).length, 6)
 
     t.deepEqual(Object.keys(promise).length, 5)
     t.true(promise instanceof Promise)
@@ -28,7 +29,7 @@ test('Api', async t => {
     t.is(typeof promise.createdAt, 'number')
 })
 
-test('Checking api req', async t => {
+test('Checking api req', async (t) => {
     const server = createNode()
     const client = createNode()
     server.open(client.message, (...args) => {
@@ -39,7 +40,7 @@ test('Checking api req', async t => {
     callServer()
 })
 
-test('Callback pattern example', async t => {
+test('Callback pattern example', async (t) => {
     const server = createNode()
     const client = createNode()
 
@@ -50,46 +51,58 @@ test('Callback pattern example', async t => {
 
     // client side
     const callServer = client.open(server.message)
-    callServer(3, 3, value => {
+    callServer(3, 3, (value) => {
         t.is(value, 9)
     })
 })
 
-test('Calling a defined function .message must return true', async t => {
+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 => {
+    const callServer = client.open((msg) => {
         t.is(server.message(msg), true)
     })
     callServer()
 })
 
-test('Calling a not defined function .message must return false', async t => {
+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 => {
+    const callServer = client.open((msg) => {
         t.is(server.message(msg), false)
     })
     callServer()
 })
 
-test('Response (reject or resolve must delete request)', async t => {
+test('Response (reject or resolve must delete request)', async (t) => {
     const server = createNode()
     const client = createNode()
-    server.open(client.message, v => {})
+    server.open(client.message, (v) => {})
     const callServer = client.open(server.message)
     t.is(Object.keys(client.requests).length, 0)
-    callServer().then(v => {
+    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 => {
+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()
+    const client = createNode({ rpcFilter: rpcFilter() })
 
     // Client side
     const callServer = client.open(
@@ -107,38 +120,19 @@ test('Passing same functions should not create a new one', async t => {
     callClient(repeated, repeated, receiveFromClient)
 })
 
-test('Sending remote functions that is from the same node must be replaced as null', async t => {
-    const server = createNode()
-    const client = createNode()
-
-    // server side
-    server.open(client.message, (...args) => {
-        t.is(args.length, 3)
-        t.is(args[0], null)
-        t.is(typeof args[1], 'function')
-        t.is(typeof args[2], 'object')
-        return args[1]
-    })
-
-    // client side
-    const callServer = client.open(server.message)
-    const resu = await callServer(callServer, () => {})
-    t.is(resu, null)
-})
-
-test('Testing messages', async t => {
-    const serdes = { serialize: m => m, deserialize: m => m }
+test('Testing messages', async (t) => {
+    const serdes = { serialize: (m) => m, deserialize: (m) => m }
     const server = createNode(serdes)
     const client = createNode(serdes)
 
     client.open(
-        msg => {
+        (msg) => {
             t.deepEqual(msg, [-1, 0, 10])
             server.message(msg)
         },
         (a, b) => a * b
     )
-    const callClient = server.open(msg => {
+    const callClient = server.open((msg) => {
         t.deepEqual(msg, [1, 0, [2, 5]])
         client.message(msg)
     })
@@ -146,33 +140,33 @@ test('Testing messages', async t => {
     t.is(ten, 10)
 })
 
-test('Escaping $f', async t => {
-    const serdes = { serialize: m => m, deserialize: m => m }
+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: { $f: 1 } }])
+        (msg) => {
+            t.deepEqual(msg, [-1, 0, { $escape: { $r: 1 } }])
             client.message(msg)
         },
-        arg => {
-            t.deepEqual(arg, { $f: 0 })
-            return { $f: 1 }
+        (arg) => {
+            t.deepEqual(arg, { $r: 0 })
+            return { $r: 1 }
         }
     )
 
     // client side
-    const callServer = client.open(msg => {
-        t.deepEqual(msg, [1, 0, [{ $escape: { $f: 0 } }]])
+    const callServer = client.open((msg) => {
+        t.deepEqual(msg, [1, 0, [{ $escape: { $r: 0 } }]])
         server.message(msg)
     })
-    const resu = await callServer({ $f: 0 })
-    t.deepEqual(resu, { $f: 1 })
+    const resu = await callServer({ $r: 0 })
+    t.deepEqual(resu, { $r: 1 })
 })
 
-test('Using resolve', async t => {
+test('Using resolve', async (t) => {
     const server = createNode()
     const client = createNode()
     server.open(client.message)
@@ -180,14 +174,14 @@ test('Using resolve', async t => {
     const callServer = client.open(server.message)
     const req = callServer()
     t.is(Object.keys(client.requests).length, 1)
-    req.then(value => {
+    req.then((value) => {
         t.is(value, 'resolved')
     })
     req.resolve('resolved')
     t.is(Object.keys(client.requests).length, 0)
 })
 
-test('Using reject', async t => {
+test('Using reject', async (t) => {
     const server = createNode()
     const client = createNode()
     server.open(client.message)
@@ -195,14 +189,14 @@ test('Using reject', async t => {
     const callServer = client.open(server.message)
     const req = callServer()
     t.is(Object.keys(client.requests).length, 1)
-    req.catch(error => {
+    req.catch((error) => {
         t.is(error, 'rejected')
     })
     req.reject('rejected')
     t.is(Object.keys(client.requests).length, 0)
 })
 
-test('Using stub', async t => {
+test('Using push', async (t) => {
     const server = createNode()
     const client = createNode()
     server.open(client.message, (...args) => {
@@ -211,30 +205,11 @@ test('Using stub', async t => {
     })
     const callServer = client.open(server.message)
     t.is(Object.keys(client.requests).length, 0)
-    t.is(callServer.stub(), undefined)
+    t.is(callServer.push(), undefined)
     t.is(Object.keys(client.requests).length, 0)
 })
 
-test('Sending remote stub functions that is from the same node must be replaced as null', async t => {
-    const server = createNode()
-    const client = createNode()
-
-    // server side
-    server.open(client.message, (...args) => {
-        t.is(args.length, 3)
-        t.is(args[0], null)
-        t.is(typeof args[1], 'function')
-        t.is(typeof args[2], 'object')
-        return args[1]
-    })
-
-    // client side
-    const callServer = client.open(server.message)
-    const resu = await callServer(callServer.stub, () => {})
-    t.is(resu, null)
-})
-
-test('Calling functions from client to server with another node in the middle', async t => {
+test('Calling functions from client to server with another node in the middle', async (t) => {
     // connection 1
     const server = createNode()
     const middleServer = createNode()
@@ -246,15 +221,15 @@ test('Calling functions from client to server with another node in the middle',
     server.open(middleServer.message, () => ({
         multiply: (a, b) => ({
             sum: (c, d) => c + d,
-            value: a * b
-        })
+            value: a * b,
+        }),
     }))
 
     // middle side
     const callServer = middleServer.open(server.message)
     const objectServer = await callServer()
     middleClient.open(client.message, () => ({
-        multiply: objectServer.multiply
+        multiply: objectServer.multiply,
     }))
 
     // client side
@@ -266,37 +241,86 @@ test('Calling functions from client to server with another node in the middle',
     t.is(result, 10)
 })
 
-test('Limiting remote functions', async t => {
-    const server = createNode({ max_remote_functions: 3 })
+test('rpc is created if rpcFilter returns the rpc itself', async (t) => {
+    const n = Math.random()
+    const server = createNode({ rpcFilter: () => n })
     const client = createNode()
-    server.open(client.message, (fn, isLast) => {
-        if (isLast) t.is(fn, null)
-        else t.is(typeof fn, 'function')
+    const callClient = server.open(client.message, (fn) => {
+        t.is(fn, n)
     })
+    t.is(callClient, n)
     const callServer = client.open(server.message)
-    t.is(server.remote_functions.size, 2)
-    await callServer(() => {}, false)
-    t.is(server.remote_functions.size, 4)
-    await callServer(() => {}, false)
-    t.is(server.remote_functions.size, 6)
-    await callServer(() => {}, true)
-    t.is(server.remote_functions.size, 6)
+    callServer(() => {})
 })
 
-test('Limiting remote functions to 0', async t => {
-    const server = createNode({ max_remote_functions: 0 })
-    const client = createNode({ max_remote_functions: 1 })
-    const callClient = server.open(client.message, fn => fn)
-    t.is(callClient, null)
+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)
-    await callServer(() => {})
-    t.is(server.remote_functions.size, 0)
-    t.is(client.remote_functions.size, 2)
-    await callServer(() => {})
-    t.is(client.remote_functions.size, 2)
-
-    // faking calls
-    t.is(server.message(JSON.stringify([111, 0])), true)
-    t.is(server.message(JSON.stringify([222, 1])), false)
-    t.is(client.message(JSON.stringify([222, 0])), false)
+    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/createStore.js b/test/create_store.js
similarity index 56%
rename from test/createStore.js
rename to test/create_store.js
index 41ed2dc..5bd10cb 100644
--- a/test/createStore.js
+++ b/test/create_store.js
@@ -1,7 +1,7 @@
 import test from 'ava'
-import { createStore } from '../'
+import { createStore, TYPE } from '../src'
 
-test('createStore', function(t) {
+test('createStore', function (t) {
     const state = { number: 1 }
     const store = createStore(state)
 
@@ -16,7 +16,7 @@ test('createStore', function(t) {
 //     t.deepEqual(state, store.getState())
 // })
 
-test('subscribe', function(t) {
+test('subscribe', function (t) {
     const state = { prop: true }
     const store = createStore(state)
     const listener = () => {}
@@ -30,7 +30,7 @@ test('subscribe', function(t) {
     t.is(store.listeners.get(listener), undefined)
 })
 
-test('unsubscribe from subscribe', function(t) {
+test('unsubscribe from subscribe', function (t) {
     const state = { prop: true }
     const store = createStore(state)
     const listener = () => {}
@@ -42,7 +42,7 @@ test('unsubscribe from subscribe', function(t) {
     t.false(store.listeners.has(listener))
 })
 
-test('unsubscribe', function(t) {
+test('unsubscribe', function (t) {
     const state = { prop: true }
     const store = createStore(state)
     const listener = () => {}
@@ -54,11 +54,11 @@ test('unsubscribe', function(t) {
     t.false(store.listeners.has(listener))
 })
 
-test('applyPatch', function(t) {
+test('applyPatch', function (t) {
     const state = { prop: false }
     const store = createStore(state)
     const patch = { prop: true }
-    const listener = p => {}
+    const listener = (p) => {}
     store.subscribe(listener)
     const outputs = store.applyPatch(patch)
     t.true(Array.isArray(outputs))
@@ -69,32 +69,37 @@ test('applyPatch', function(t) {
     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('patchAndEmit', function(t) {
-//     const store = createStore({ prop: false })
-//     const patch = { prop: true, newprop: true }
-//     const toreturnfromlistener = {}
-//     const listener = p => {
-//         t.not(p, patch)
-//         t.deepEqual(p, patch)
-//         return toreturnfromlistener
-//     }
-//     store.subscribe(listener)
-//     const outputs = store.patchAndEmit(patch)
-//     t.true(Array.isArray(outputs))
-//     t.is(outputs.length, 1)
-//     t.is(outputs[0], toreturnfromlistener)
-// })
-
-test('subscribe filter', function(t) {
-    const store = createStore({ prop: false })
+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'
+    const filter = (mutation) => mutation.prop !== 'newprop'
     store.subscribe(() => {}, filter)
     const result = store.applyPatch(patch)
     const [output] = result
@@ -105,7 +110,7 @@ test('subscribe filter', function(t) {
     t.is(result.mutations.length, 2)
 })
 
-test('subscribe output of applyPatch must return same length that listeners', function(t) {
+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(
@@ -123,3 +128,36 @@ test('subscribe output of applyPatch must return same length that listeners', fu
     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/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/merge.js b/test/merge.js
index c16cb6e..2375632 100644
--- a/test/merge.js
+++ b/test/merge.js
@@ -1,45 +1,55 @@
 import test from 'ava'
-import { merge } from '../'
+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(merge, 'dop')
+run(dop.merge, 'dop')
 run(_.merge, 'lodash')
 
 function run(merge, name) {
-    test(name + ': should merge `source` into `object`', function(t) {
+    test(name + ': should merge `source` into `object`', function (t) {
         var names = {
-            characters: [{ name: 'barney' }, { name: 'fred' }]
+            characters: [{ name: 'barney' }, { name: 'fred' }],
         }
 
         var ages = {
-            characters: [{ age: 36 }, { age: 40 }]
+            characters: [{ age: 36 }, { age: 40 }],
         }
 
         var heights = {
-            characters: [{ height: '5\'4"' }, { height: '5\'5"' }]
+            characters: [{ height: '5\'4"' }, { height: '5\'5"' }],
         }
 
         var expected = {
             characters: [
                 { name: 'barney', age: 36, height: '5\'4"' },
-                { name: 'fred', age: 40, height: '5\'5"' }
-            ]
+                { name: 'fred', age: 40, height: '5\'5"' },
+            ],
         }
 
         t.deepEqual(merge(names, ages, heights), expected)
     })
 
-    test(name + ': should work with four arguments', function(t) {
+    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) {
+    test(name + ': should merge onto function `object` values', function (t) {
         function Foo() {}
 
         var source = { a: 1 },
@@ -51,8 +61,8 @@ function run(merge, name) {
 
     test(
         name + ': should merge first source object properties to function',
-        function(t) {
-            var fn = function() {},
+        function (t) {
+            var fn = function () {},
                 object = { prop: {} },
                 actual = merge({ prop: fn }, object)
 
@@ -63,8 +73,8 @@ function run(merge, name) {
     test(
         name +
             ': should merge first and second source object properties to function',
-        function(t) {
-            var fn = function() {},
+        function (t) {
+            var fn = function () {},
                 object = { prop: {} },
                 actual = merge({ prop: fn }, { prop: fn }, object)
 
@@ -72,22 +82,23 @@ function run(merge, name) {
         }
     )
 
-    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)
+    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))
+            t.deepEqual(actual, expected)
+            t.true(!('b' in source1.a))
 
-        actual = merge(source1, source2)
-        t.deepEqual(actual, expected)
-    })
+            actual = merge(source1, source2)
+            t.deepEqual(actual, expected)
+        }
+    )
 
-    test(name + ': should merge onto non-plain `object` values', function(t) {
+    test(name + ': should merge onto non-plain `object` values', function (t) {
         function Foo() {}
 
         var object = new Foo(),
@@ -97,7 +108,7 @@ function run(merge, name) {
         t.deepEqual(object.a, 1)
     })
 
-    test(name + ': should treat sparse array sources as dense', function(t) {
+    test(name + ': should treat sparse array sources as dense', function (t) {
         var array = [1]
         array[2] = 3
 
@@ -110,7 +121,7 @@ function run(merge, name) {
         t.deepEqual(actual, expected)
     })
 
-    test(name + ': should assign `null` values', function(t) {
+    test(name + ': should assign `null` values', function (t) {
         var actual = merge({ a: 1 }, { a: null })
         t.deepEqual(actual.a, null)
     })
@@ -118,7 +129,7 @@ function run(merge, name) {
     test(
         name +
             ': should assign non array/buffer/typed-array/plain-object source values directly',
-        function(t) {
+        function (t) {
             function Foo() {}
 
             var values = [
@@ -128,11 +139,11 @@ function run(merge, name) {
                     Foo,
                     new Number(),
                     new String(),
-                    new RegExp()
+                    new RegExp(),
                 ],
                 expected = _.map(values, () => true)
 
-            var actual = _.map(values, function(value) {
+            var actual = _.map(values, function (value) {
                 var object = merge({}, { a: value, b: { c: value } })
                 return object.a === value && object.b.c === value
             })
@@ -140,7 +151,7 @@ function run(merge, name) {
         }
     )
 
-    test(name + ': should not augment source objects', function(t) {
+    test(name + ': should not augment source objects', function (t) {
         var source1 = { a: [{ a: 1 }] },
             source2 = { a: [{ b: 2 }] },
             actual = merge({}, source1, source2)
@@ -158,40 +169,41 @@ function run(merge, name) {
         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)
-        }
+    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)
+            var object = { a: 1 },
+                actual = merge(new Foo(), object)
 
-        t.true(actual instanceof Foo)
-        t.deepEqual(actual, 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)])
-    })
+            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) {
+        function (t) {
             var object = {},
                 pass = true
 
             Object.defineProperty(object, 'a', {
                 configurable: true,
                 enumerable: true,
-                get: function() {
+                get: function () {
                     pass = false
                 },
-                set: function() {
+                set: function () {
                     pass = false
-                }
+                },
             })
 
             merge(object, object)
@@ -202,7 +214,7 @@ function run(merge, name) {
     test(
         name +
             ': should convert strings to arrays when merging arrays of `source`',
-        function(t) {
+        function (t) {
             var object = { a: 'abcde' },
                 actual = merge(object, { a: ['x', 'y', 'z'] })
 
@@ -210,7 +222,7 @@ function run(merge, name) {
         }
     )
 
-    test(name + ': checking different types', function(t) {
+    test(name + ': checking different types', function (t) {
         const object = {
             string: 'string',
             boolean: true,
@@ -225,7 +237,7 @@ function run(merge, name) {
             date: new Date(),
             regexp: /molamazo/g,
             f: () => {},
-            function: function() {
+            function: function () {
                 console.log(arguments)
             },
             obj: { lolo: 111 },
@@ -243,10 +255,10 @@ function run(merge, name) {
                             d221: 12,
                             d223: {
                                 hola: 'hola',
-                                undefined: undefined
-                            }
-                        }
-                    }
+                                undefined: undefined,
+                            },
+                        },
+                    },
                 },
                 arrobj: ['a', 'b', 'c', 'd'],
                 g: 123,
@@ -254,11 +266,11 @@ function run(merge, name) {
                     d22: {
                         d222: 25,
                         d223: {
-                            hola: 'mundo'
-                        }
-                    }
-                }
-            }
+                            hola: 'mundo',
+                        },
+                    },
+                },
+            },
         }
 
         var actual = merge({}, object)
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/rpc.js b/test/rpcs.js
similarity index 65%
rename from test/rpc.js
rename to test/rpcs.js
index d333c94..5b0efeb 100644
--- a/test/rpc.js
+++ b/test/rpcs.js
@@ -1,143 +1,170 @@
 import test from 'ava'
-import { createNode } from '../'
+import { createNode } from '../src'
 
 const localFunctions = {
-    okSync: function(req) {
+    okSync: function (req) {
         return 'Ok'
     },
-    okAsync: async function(req) {
+    okAsync: async function (req) {
         return 'Ok'
     },
-    okPromise: function(req) {
-        return new Promise(function(resolve, reject) {
+    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) {
+    undefinedSync: function (req) {},
+    undefinedAsync: async function (req) {},
+    undefinedPromise: function (req) {
+        return new Promise(function (resolve, reject) {
             resolve(undefined)
         })
     },
 
-    errorSync: function(req) {
+    errorSync: function (req) {
         throw 'Error'
     },
-    errorAsync: async function(req) {
+    errorAsync: async function (req) {
         throw 'Error'
     },
-    errorPromise: function(req) {
-        return new Promise(function(resolve, reject) {
+    errorPromise: function (req) {
+        return new Promise(function (resolve, reject) {
             reject('Error')
         })
     },
 
-    timeoutSync: function(req) {
-        setTimeout(function() {
+    timeoutSync: function (req) {
+        setTimeout(function () {
             req.resolve('Ok')
         }, 10)
         return req
     },
-    timeoutAsync: async function(req) {
-        setTimeout(function() {
+    timeoutAsync: async function (req) {
+        setTimeout(function () {
             req.resolve('Ok')
         }, 10)
         return req
     },
-    timeoutPromise: async function(req) {
-        return new Promise(function(resolve, reject) {
-            setTimeout(function() {
+    timeoutPromise: async function (req) {
+        return new Promise(function (resolve, reject) {
+            setTimeout(function () {
                 req.resolve('Ok')
             }, 10)
             resolve(req)
         })
     },
 
-    reqresolveSync: function(req) {
+    reqresolveSync: function (req) {
         return req.resolve('ok')
     },
-    reqresolveAsync: async function(req) {
+    reqresolveAsync: async function (req) {
         return req.resolve('ok')
     },
-    reqresolvePromise: function(req) {
-        return new Promise(function(resolve, reject) {
+    reqresolvePromise: function (req) {
+        return new Promise(function (resolve, reject) {
             resolve(req.resolve('ok'))
         })
     },
 
-    presolveSync: function(req) {
+    presolveSync: function (req) {
         return Promise.resolve('ok')
     },
-    presolveAsync: async function(req) {
+    presolveAsync: async function (req) {
         return Promise.resolve('ok')
     },
-    presolvePromise: function(req) {
-        return new Promise(function(resolve, reject) {
+    presolvePromise: function (req) {
+        return new Promise(function (resolve, reject) {
             resolve(Promise.resolve('ok'))
         })
     },
 
-    reqrejectSync: function(req) {
+    reqrejectSync: function (req) {
         return req.reject('Error')
     },
-    reqrejectAsync: async function(req) {
+    reqrejectAsync: async function (req) {
         return req.reject('Error')
     },
-    reqrejectPromise: function(req) {
-        return new Promise(function(resolve, reject) {
+    reqrejectPromise: function (req) {
+        return new Promise(function (resolve, reject) {
             reject(req.reject('Error'))
         })
     },
 
-    prejectSync: function(req) {
+    prejectSync: function (req) {
         return Promise.reject('Error')
     },
-    prejectAsync: async function(req) {
+    prejectAsync: async function (req) {
         return Promise.reject('Error')
     },
-    prejectPromise: function(req) {
-        return new Promise(function(resolve, reject) {
+    prejectPromise: function (req) {
+        return new Promise(function (resolve, reject) {
             resolve(Promise.reject('Error'))
         })
     },
 
-    referenceError: function() {
+    referenceError: function () {
         functionThatDoesNotExists()
     },
 
-    throwError: function() {
+    throwError: function () {
         throw new Error('whatever')
     },
 
-    throwUndefined: function() {
+    throwErrorAsync: async function () {
+        throw new Error('async whatever')
+    },
+
+    throwErrorAsyncRemote: async function () {
+        throw new Error('async whatever')
+    },
+
+    throwUndefined: function () {
         throw undefined
     },
 
-    throwNull: function() {
+    throwNull: function () {
         throw null
     },
 
-    throwZero: function() {
+    throwZero: function () {
         throw 0
-    }
+    },
 }
 
 const server = createNode()
 const client = createNode()
 let remoteFunctions
-client.open(server.message, r => {
+client.open(server.message, (r) => {
     remoteFunctions = r
 })
 const callClient = server.open(client.message)
 callClient(localFunctions)
 
-test('Matching same values', async function(t) {
+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) {
+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())
@@ -145,7 +172,7 @@ test('Ok', async function(t) {
     t.is(await remoteFunctions.okPromise(), await localFunctions.okPromise())
 })
 
-test('Error', async function(t) {
+test('Error', async function (t) {
     t.is(1, 1)
     try {
         await remoteFunctions.errorSync()
@@ -186,7 +213,7 @@ test('Error', async function(t) {
     }
 })
 
-test('timeout', async function(t) {
+test('timeout', async function (t) {
     t.is(
         await remoteFunctions.timeoutSync(),
         await remoteFunctions.timeoutAsync()
@@ -197,7 +224,7 @@ test('timeout', async function(t) {
     )
 })
 
-test('req.resolve', async function(t) {
+test('req.resolve', async function (t) {
     t.is(
         await remoteFunctions.reqresolveSync(),
         await remoteFunctions.reqresolveAsync()
@@ -208,7 +235,7 @@ test('req.resolve', async function(t) {
     )
 })
 
-test('Promise.resolve', async function(t) {
+test('Promise.resolve', async function (t) {
     t.is(
         await remoteFunctions.reqresolveSync(),
         await remoteFunctions.reqresolveAsync()
@@ -219,7 +246,7 @@ test('Promise.resolve', async function(t) {
     )
 })
 
-test('req.reject', async function(t) {
+test('req.reject', async function (t) {
     try {
         await remoteFunctions.reqrejectSync()
         t.is(1, 2, 'this should not happen')
@@ -240,7 +267,7 @@ test('req.reject', async function(t) {
     }
 })
 
-test('Promise.reject', async function(t) {
+test('Promise.reject', async function (t) {
     try {
         await remoteFunctions.prejectSync()
         t.is(1, 2, 'this should not happen')
@@ -261,7 +288,7 @@ test('Promise.reject', async function(t) {
     }
 })
 
-test('Undefined', async function(t) {
+test('Undefined', async function (t) {
     t.is(
         await remoteFunctions.undefinedSync(),
         await remoteFunctions.undefinedAsync()
@@ -284,7 +311,7 @@ test('Undefined', async function(t) {
     )
 })
 
-test('throw "Error"', async function(t) {
+test('throw "Error"', async function (t) {
     try {
         remoteFunctions.errorSync().catch(() => {})
         t.true(true)
@@ -299,7 +326,7 @@ test('throw "Error"', async function(t) {
     }
 })
 
-test('ReferenceError error', async function(t) {
+test('ReferenceError error', async function (t) {
     try {
         remoteFunctions.referenceError()
         t.true(false)
@@ -314,7 +341,7 @@ test('ReferenceError error', async function(t) {
     }
 })
 
-test('throw new Error()', async function(t) {
+test('throw new Error()', async function (t) {
     try {
         localFunctions.throwError()
         t.true(false)
@@ -329,7 +356,7 @@ test('throw new Error()', async function(t) {
     }
 })
 
-test('throwUndefined', async function(t) {
+test('throwUndefined', async function (t) {
     try {
         localFunctions.throwUndefined()
         t.true(false)
@@ -344,7 +371,7 @@ test('throwUndefined', async function(t) {
     }
 })
 
-test('throwNull', async function(t) {
+test('throwNull', async function (t) {
     try {
         localFunctions.throwNull()
         t.true(false)
@@ -359,7 +386,7 @@ test('throwNull', async function(t) {
     }
 })
 
-test('throwZero', async function(t) {
+test('throwZero', async function (t) {
     try {
         localFunctions.throwZero()
         t.true(false)
diff --git a/test/type_delete.js b/test/type_delete.js
index f2d9eb6..998e42d 100644
--- a/test/type_delete.js
+++ b/test/type_delete.js
@@ -1,100 +1,100 @@
 import test from 'ava'
-import { decode, TYPE } from '../'
+import { decode, TYPE } from '../src'
 import { testEncodeDecode, testPatchUnpatch } from './utils'
 const DELETE = TYPE.Delete()
 
-test('Valid type', function(t) {
+test('Valid type', function (t) {
     const patch = { convert: DELETE }
     const expected = { convert: { $d: 0 } }
     testEncodeDecode(t, patch, expected)
 })
 
-test('Escape', function(t) {
+test('Escape', function (t) {
     const patch = { escape: { $d: 0 } }
     const expected = { escape: { $escape: { $d: 0 } } }
     testEncodeDecode(t, patch, expected)
 })
 
-test('Ignore', function(t) {
+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) {
+test('$escaping', function (t) {
     const patch = {
         convert: DELETE,
-        escape: { $d: 0 }
+        escape: { $d: 0 },
     }
     const expected = {
         convert: { $d: 0 },
-        escape: { $escape: { $d: 0 } }
+        escape: { $escape: { $d: 0 } },
     }
     testEncodeDecode(t, patch, expected)
 })
 
-test('This should not be escaped because $d has another valid prop', function(t) {
+test('This should not be escaped because $d has another valid prop', function (t) {
     const patch = {
         convert: DELETE,
-        ignored: { $d: 0, $escape: 0 }
+        ignored: { $d: 0, $escape: 0 },
     }
     const expected = {
         convert: { $d: 0 },
-        ignored: { $d: 0, $escape: 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) {
+test('This should not be escaped because $d has another valid prop 2', function (t) {
     const patch = {
         convert: DELETE,
-        escape: { $escape: 0, $d: DELETE }
+        escape: { $escape: 0, $d: DELETE },
     }
     const expected = {
         convert: { $d: 0 },
-        escape: { $escape: 0, $d: { $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) {
+test('This should not be escaped because $d is not an number', function (t) {
     const patch = {
         convert: DELETE,
-        escape: { $d: 'string' }
+        escape: { $d: 'string' },
     }
     const expected = {
         convert: { $d: 0 },
-        escape: { $d: 'string' }
+        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) {
+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) {
+test('Decode alone', function (t) {
     const patch = {
         convert: { $d: 0 },
         string: { $d: 'string' },
         escape: { $escape: { $d: 0 } },
-        ignore: { $escape: { $d: 0 }, two: { $d: 0 } }
+        ignore: { $escape: { $d: 0 }, two: { $d: 0 } },
     }
     const expected = {
         convert: DELETE,
         string: { $d: 'string' },
         escape: { $d: 0 },
-        ignore: { $escape: DELETE, two: DELETE }
+        ignore: { $escape: DELETE, two: DELETE },
     }
     t.deepEqual(decode(patch), expected)
 })
 
-test('patch', function(t) {
+test('patch', function (t) {
     const target = { value: 12345 }
     const patch = { value: TYPE.Delete() }
     const expected = {}
 
-    testPatchUnpatch(t, target, patch, expected)
+    testPatchUnpatch({ t, target, patch, expected })
 })
diff --git a/test/type_inner.js b/test/type_inner.js
deleted file mode 100644
index 4f598d1..0000000
--- a/test/type_inner.js
+++ /dev/null
@@ -1,140 +0,0 @@
-import test from 'ava'
-import { TYPE } from '../'
-import { testEncodeDecode, testPatchUnpatch } from './utils'
-
-test('Valid type', function(t) {
-    const patch = { convert: TYPE.Inner({ 0: 'world' }) }
-    const expected = { convert: { $i: { 0: 'world' } } }
-    testEncodeDecode(t, patch, expected)
-})
-
-test('Sub Delete', function(t) {
-    const patch = { convert: TYPE.Inner({ 0: { a: TYPE.Delete() } }) }
-    const expected = { convert: { $i: { 0: { a: { $d: 0 } } } } }
-    testEncodeDecode(t, patch, expected)
-})
-
-test('Sub Inner', function(t) {
-    const patch = { convert: TYPE.Inner({ 0: { a: TYPE.Inner({ 0: 'b' }) } }) }
-    const expected = { convert: { $i: { 0: { a: { $i: { 0: 'b' } } } } } }
-    testEncodeDecode(t, patch, expected)
-})
-
-test('Escape', function(t) {
-    const patch = { escape: { $i: { 1: 2 } } }
-    const expected = { escape: { $escape: { $i: { 1: 2 } } } }
-    testEncodeDecode(t, patch, expected)
-})
-
-test('Ignore', function(t) {
-    const patch = { ignore: { $i: [0, 1] } }
-    const expected = { ignore: { $i: [0, 1] } }
-    testEncodeDecode(t, patch, expected)
-})
-
-test('Inner only accepts plain objects', function(t) {
-    try {
-        TYPE.Inner([])
-    } catch (e) {
-        t.is(typeof e, 'string')
-    }
-})
-
-test('API', function(t) {
-    const target = { array: ['a'] }
-    const patch = { array: TYPE.Inner({ 1: '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.Inner)
-    t.deepEqual(unpatch.array.patch, { '1': TYPE.Delete(), length: 1 })
-})
-
-test('Patching a non array must do nothing', function(t) {
-    const target = { array: 1234 }
-    const patch = { array: TYPE.Inner({ 0: 'b' }) }
-    const expected = { array: 1234 }
-    testPatchUnpatch(t, target, patch, expected)
-    const { mutations } = testPatchUnpatch(t, target, patch, expected)
-    t.is(mutations.length, 0)
-})
-
-test('Editing top level', function(t) {
-    const target = { array: ['a', 'b', 'c'] }
-    const patch = { array: TYPE.Inner({ 0: 'A', 3: 'd' }) }
-    const expected = { array: ['A', 'b', 'c', 'd'] }
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('Editing subobjects', function(t) {
-    const target = { array: [{ a: false }] }
-    const patch = { array: TYPE.Inner({ 0: { a: true } }) }
-    const expected = { array: [{ a: true }] }
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('Deleting subobject', function(t) {
-    const target = { array: [{ a: true }] }
-    const patch = { array: TYPE.Inner({ 0: { a: TYPE.Delete() } }) }
-    const expected = { array: [{}] }
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('Updating sub-subarray', function(t) {
-    const target = { array: [{ subarray: ['a'] }] }
-    const array = target.array
-    const subarray = target.array[0].subarray
-    const patch = {
-        array: TYPE.Inner({ 0: { subarray: TYPE.Inner({ 0: 'b' }) } })
-    }
-    const expected = { array: [{ subarray: ['b'] }] }
-    testPatchUnpatch(t, target, patch, expected)
-    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: TYPE.Inner({ 0: { subarray: ['b', 'c'] } })
-    }
-    const expected = { array: [{ subarray: ['b', 'c'] }] }
-    testPatchUnpatch(t, target, patch, expected)
-    t.is(array, target.array)
-    t.not(subarray, target.array[0].subarray)
-})
-
-test('Pushing subobjects', function(t) {
-    const target = ['A']
-    const patch = TYPE.Inner({ 2: { a: true } })
-    const expected = ['A', undefined, { a: true }]
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('Editing subobject and creating a new one', function(t) {
-    const target = [{ a: false }]
-    const patch = TYPE.Inner({ 0: { a: true }, 1: { b: true } })
-    const expected = [{ a: true }, { b: true }]
-    testPatchUnpatch(t, target, patch, expected)
-})
-
-test('Encoding unpatch', function(t) {
-    const target = [{ a: false }]
-    const patch = TYPE.Inner({ 0: { a: true }, 1: { b: true } })
-    const expected = [{ a: true }, { b: true }]
-    const { unpatch } = testPatchUnpatch(t, target, patch, expected)
-    // console.log(unpatch.patch[1])
-    testEncodeDecode(t, unpatch.patch, {
-        '0': { a: false },
-        '1': { $d: 0 },
-        length: 1
-    })
-})
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
index 96251b5..8cf9370 100644
--- a/test/type_replace.js
+++ b/test/type_replace.js
@@ -1,135 +1,158 @@
 import test from 'ava'
-import { decode, TYPE } from '../'
+import { decode, TYPE } from '../src'
 import { testEncodeDecode, testPatchUnpatch } from './utils'
 
-test('Valid type', function(t) {
+test('Valid type', function (t) {
     const patch = { convert: TYPE.Replace({ hello: 'world' }) }
-    const expected = { convert: { $r: { hello: 'world' } } }
+    const expected = { convert: { $e: { hello: 'world' } } }
     testEncodeDecode(t, patch, expected)
 })
 
-test('Escape', function(t) {
-    const patch = { escape: { $r: 1 } }
-    const expected = { escape: { $escape: { $r: 1 } } }
+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: { $r: 1, $other: 1 } }
-    const expected = { ignore: { $r: 1, $other: 1 } }
+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) {
+test('$escaping', function (t) {
     const patch = {
         convert: TYPE.Replace([1, 2, 3]),
-        escape: { $r: 1 }
+        escape: { $e: 1 },
     }
     const expected = {
-        convert: { $r: [1, 2, 3] },
-        escape: { $escape: { $r: 1 } }
+        convert: { $e: [1, 2, 3] },
+        escape: { $escape: { $e: 1 } },
     }
     testEncodeDecode(t, patch, expected)
 })
 
-test('This should not be escaped because $r has another valid prop', function(t) {
+test('This should not be escaped because $e has another valid prop', function (t) {
     const patch = {
         convert: TYPE.Replace([1, 2, 3]),
-        ignored: { $r: [1, 2, 3], $escape: [1, 2, 3] }
+        ignored: { $e: [1, 2, 3], $escape: [1, 2, 3] },
     }
     const expected = {
-        convert: { $r: [1, 2, 3] },
-        ignored: { $r: [1, 2, 3], $escape: [1, 2, 3] }
+        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 $r has another valid prop 2', function(t) {
+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], $r: TYPE.Replace([1, 2, 3]) }
+        escape: { $escape: [1, 2, 3], $e: TYPE.Replace([1, 2, 3]) },
     }
     const expected = {
-        convert: { $r: [1, 2, 3] },
-        escape: { $escape: [1, 2, 3], $r: { $r: [1, 2, 3] } }
+        convert: { $e: [1, 2, 3] },
+        escape: { $escape: [1, 2, 3], $e: { $e: [1, 2, 3] } },
     }
     testEncodeDecode(t, patch, expected)
 })
 
-test('Decode alone', function(t) {
+test('Decode alone', function (t) {
     const patch = {
-        convert: { $r: [1, 2, 3] },
-        string: { $r: 'string' },
-        escape: { $escape: { $r: [1, 2, 3] } },
+        convert: { $e: [1, 2, 3] },
+        string: { $e: 'string' },
+        escape: { $escape: { $e: [1, 2, 3] } },
         ignore: {
-            $escape: { $r: [1, 2, 3] },
-            two: { $r: [1, 2, 3] }
-        }
+            $escape: { $e: [1, 2, 3] },
+            two: { $e: [1, 2, 3] },
+        },
     }
     const expected = {
         convert: TYPE.Replace([1, 2, 3]),
         string: TYPE.Replace('string'),
-        escape: { $r: [1, 2, 3] },
+        escape: { $e: [1, 2, 3] },
         ignore: {
             $escape: TYPE.Replace([1, 2, 3]),
-            two: TYPE.Replace([1, 2, 3])
-        }
+            two: TYPE.Replace([1, 2, 3]),
+        },
     }
     t.deepEqual(decode(patch), expected)
 })
 
-test('patch array', function(t) {
+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)
+    testPatchUnpatch({ t, target, patch, expected })
 })
 
-test('patch object', function(t) {
+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)
+    testPatchUnpatch({ t, target, patch, expected })
 })
 
-test('patch should replace the complete object', function(t) {
+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, false)
+    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) {
+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)
+    testPatchUnpatch({ t, target, patch, expected })
     t.is(copyvalue, target.value)
     t.deepEqual(copyvalue, target.value)
 })
 
-test('replace array', function(t) {
+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)
+    testPatchUnpatch({ t, target, patch, expected })
 })
 
-test('same behavior as replace array', function(t) {
+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)
+    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
index d97ddd5..7b0a2af 100644
--- a/test/type_splice.js
+++ b/test/type_splice.js
@@ -1,84 +1,84 @@
 import test from 'ava'
-import { applyPatch } from '../'
-import { TYPE } from '../'
+import { applyPatch } from '../src'
+import { TYPE } from '../src'
 import { testEncodeDecode, testPatchUnpatch } from './utils'
 
-test('Valid type', function(t) {
+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) {
+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) {
+test('Ignore', function (t) {
     const patch = { ignore: { $s: { a: 2 } } }
     const expected = { ignore: { $s: { a: 2 } } }
     testEncodeDecode(t, patch, expected)
 })
 
-test('API', function(t) {
+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(
+    const { unpatch, result, mutations } = testPatchUnpatch({
         t,
         target,
         patch,
-        expected
-    )
+        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) {
+test('Patching a non array must do nothing', function (t) {
     const target = { array: 1234 }
-    const patch = { array: TYPE.Splice({ 0: 'b' }) }
+    const patch = { array: TYPE.Splice(0, 0, 5678) }
     const expected = { array: 1234 }
-    const { mutations } = testPatchUnpatch(t, target, patch, expected)
+    const { mutations } = testPatchUnpatch({ t, target, patch, expected })
     t.is(mutations.length, 0)
 })
 
-test('removing and adding', function(t) {
+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)
+    const { mutations } = testPatchUnpatch({ t, target, patch, expected })
     t.is(mutations.length, 1)
 })
 
-test('removing', function(t) {
+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)
+    testPatchUnpatch({ t, target, patch, expected })
 })
 
-test('removing middle', function(t) {
+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)
+    testPatchUnpatch({ t, target, patch, expected })
 })
 
-test('adding', function(t) {
+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)
+    testPatchUnpatch({ t, target, patch, expected })
 })
 
-test('negative', function(t) {
+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, false)
+    testPatchUnpatch({ t, target, patch, expected, reverse: false })
 })
 
 function testAgainstNative(t, original, params) {
@@ -96,123 +96,131 @@ function testAgainstNative(t, original, params) {
     // console.log({ result: target })
 }
 
-test('1/ vs Array.splice', function(t) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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/utils.js b/test/utils.js
index 592d32a..7ebbf91 100644
--- a/test/utils.js
+++ b/test/utils.js
@@ -1,6 +1,6 @@
-import { applyPatch, encode, decode } from '../'
-import { getNewPlain } from '../src/util/get'
-import { isPlainObject } from '../src/util/is'
+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)
@@ -17,6 +17,12 @@ function isInteger(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
 }
@@ -31,16 +37,33 @@ function testEncodeDecode(
     const encoded = serializeDeserialize(encode(patch), serialize)
     t.deepEqual(expected, encoded)
     t.not(patch, encoded)
+    const decoded = decode(serializeDeserialize(encoded, serialize))
     if (reverse) {
-        const decoded = decode(serializeDeserialize(encoded, serialize))
         t.deepEqual(patch, decoded)
         t.not(encoded, decoded)
     }
+    return { encoded, decoded }
 }
 
-function testPatchUnpatch(t, target, patch, expected, reverse = true) {
+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, patch)
+    const output = applyPatch(
+        target,
+        encodeDecode(patch, encodedecode, serialize)
+    )
     const { unpatch, mutations, result } = output
     if (isPlainObject(result) && isPlainObject(target)) {
         t.is(target, result)
@@ -48,10 +71,30 @@ function testPatchUnpatch(t, target, patch, expected, reverse = true) {
     target = result
     t.deepEqual(target, expected)
     if (reverse) {
-        const output2 = applyPatch(target, unpatch)
-        t.deepEqual(output2.result, cloned)
+        const output2 = applyPatch(
+            target,
+            encodeDecode(unpatch, encodedecode, serialize)
+        )
+        t.deepEqual(output2.result, cloned, '(Unpatching)')
     }
-    return { unpatch, mutations, result }
+
+    // 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 }
