npm package: @rosey-ai/merge
Description: Merge your front end and back end development experience.
Author: Scott Lindeneau
Latest version: 0.0.7

# Merge
**Merge your front end and back end development experience.**

A small library that allows you to invoke functions from your server to your client or vice versa.

Merge is designed to work with any system that provides a *message pump style information transfer*. We specifically built it around socket.io but other message pumps should work as well (*such as webworkers*).

# Installation
Using yarn:
```
yarn add @rosey-ai/Merge
```
Using npm:

```
npm -i --save @rosey-ai/Merge
```

# Exposing Functions
Merge uses promises internally to manage the inherient asyncronous nature of these systems. Thus when you expose a function there are a couple of requirements.

1) Writing the exposed function (The function that will be called) must be written taking two callbacks, the resolve and reject handlers for the promise. The exposed function can take any number of arguments, provided that the last two arguments are the resolve and reject handlers. For example:
```javascript
function CallMe(one,two,resolve,reject){
  if ( ... ){
    // We succeeded and want to return that success
    resolve({"return":"values","go":"here"})
  }else{
    // We failed or errored and want to call that reject
    reject(["something","went wrong"])
  }
}
```

Your resolve and reject handlers can 'return' any kind of data.

2) Calling the exposed function. When calling the function you *must* pass the correct number of arguments or merge will break. This is a a known bug. Otherwise you can use `.then` promise notation to complete. `await` also works.
```
ToDo: Reject correctly on incorrect number of arguments passed
```
For example:
```javascript
merge[id].CallMe("First",{second:"args"}).then(
  (result)=>{
    console.log(result);
  },(error)=>{
    console.log(error);    
  })
```
OR
```javascript
  let result = await merge[id].CallMe("First",{second:"args"});
```


# Frontend & Backend
Frontend script is designed to be loaded as an es6 module, either through a `<script>` tag or with a dynmaic module `import`.

Backend script is designed to be `required` by a node application.

Both frontend and backend modules are designed to be "singleton" instances that live inside of some other singleton style object. You can create multiple instances by passing in other parent objects. (On the frontend this could be `window` on the backend it could be your `app` (or some object you `set/get` in your app))

Multiple message pumps can be stored and utilized inside a single Merge instance, each with their own set of exposed functions.

# Example Usage
Frontend (Browser)

```javascript
<script type="text/javascript" src="/@rosey-ai/merge/frontend/Merge.js"></script>
<script>
  let io_pump = io("/merge-pump");
  let the_server = Merge(window);
  the_server["example"] = io_pump;
  //Declare a function for the server to be able to call.
  the_server["example"] .CallMeSomeTime = (args,resolve,reject)=>{
                console.log("The server called me: ",args);
                resolve("Argumens and results can be of any serializeable type.");
            };
the_server["example"].DoStuff(data:{msg:"Hello"},stuff:[1,2,3],"third argument").then(
  (results)=>{
    console.log("The server finished and sent back: ",results)
  });
</script>
```

Backend (Node)

```javascript
let merge = require("@rosey-ai/merge")(app);
let io_pump = io.of('/merge-pump');
io_pump.on("connection",(socket)=>{
  //Each socket communicates over a unique room. We could use the default room, but you can specify a room as well.
  //This way only messages for the intended target are sent and recieved.
  //This also means that if all of a single users connections from multiple devices are placed in the same room, all of the devices
  //will be kept in sync.
    socket.join(socket.id);
    //Its possible that this socket is simply reconnecting due to some network error. It should just be in the same room.
    // (or a single user might log in with multiple devices)
    if(merge[socket.id] == undefined){
      //Each user gets a unique outgoing room. (This will notify everyone in that room)
      merge[socket.id] = io_pump.to(socket.id);
    }
    //We need to know about each client connection.
    merge[socket.id] = socket;
    /* Function just for this socket */
    merge[socket.id].DoStuff = (first,second,third,resolve,reject)=>{
        console.log("The client told me to do stuff: ",args);
        //The server can do some time intesive processing if it wants.
        setTimeout(()=>{resolve("I waited for you");},3000);
    };
    /* Declaring a function for all sockets that can be called from any client */
    merge.DoOtherStuff = (first,second,third,resolve,reject)=>{
            console.log("The client told me to other do stuff: ",args);
            //The server can do some time intesive processing if it wants.
            setTimeout(()=>{resolve("I waited for you");},3000);
      };
    merge[socket.id].CallMeSomeTime(["Args",{can:"be"},"anything","serializable"]).then((result)=>{
        console.log("The client returned",result);
    });
  });
```