# ${pkg.name} ${pkg.description} ${badge('npm')} ${badge('circleci')} [![Coverage Status](https://coveralls.io/repos/github/OakLabsInc/oak-tools/badge.svg?branch=master)](https://coveralls.io/github/OakLabsInc/oak-tools?branch=master) ${badge('npm-license')} [![Standard - JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-green.svg)](http://standardjs.com/) ${badge('nodei')} ## Install `npm install --save ${pkg.name}` ## Documentation Head on over to the [documentation](https://oaklabsinc.github.io/oak-tools/) page! ## Usage `oak-tools` consists of a few helpful utility methods. Everything exposed from the module is a `Class` and needs to be constructed using `new`, except the `logger` **logger()** The logger is based off [pino](https://github.com/pinojs/pino) and has nearly the same same option parameters. The following example are the default options. ```javascript const { logger } = require('oak-tools') let log = logger({ // these are the default properties level: 'info', stream: process.stdout, pretty: false }) log.info('sup') log.error({ err: new Error('crap...') }) log.debug({ msg: 'debug message here', now: Date.now() }) ``` **server(_type_)** The server is extended from a good 'ol [EventEmitter2](https://github.com/asyncly/EventEmitter2). Head [here](https://oaklabsinc.github.io/oak-tools/WebSocketServer.html) for full documentation on the `server` class ```javascript const msgpack = require('msgpack5')() const Server = require('oak-tools').server('websocket') // all these options are, well... optional let server = new Server({ // these are the default properties port: 9500, host: 'localhost', serializer: { encode, decode } = msgpack }) server .on('error', function (err) { console.error('* client error', err) }) .on('connection', function (ID) { console.log('* client connection: ', ID) }) .on('reconnect', function (ID) { console.log('* client reconnect: ', ID) }) .on('close', function (ID) { console.log('* client closed', ID) }) .on('who.is.the.man.now', function () { server.pub('heres.who', { answer: 'you are. http://www.yourethemannowdog.com/' }) }) ``` **client(_type_)** The `client` API is similar to `server`, but it has both a `sub` and a `pub` method. ```javascript const { join } = require('path') let Client = require(join(__dirname, '..')).client('websocket') let client = new Client({ id: 'sean connery' // defaults to random UUID }) client.on('ready', function () { console.log('* client is connected') client .on('heres.who', function ({ answer }) { console.log(answer) }) .pub('who.is.the.man.now') }) ``` ## Message Rationale & Flow The goal of the server/client is to wrap up all the higher level messaging operations to be more protocol agnostic (as well as non-repetitive). This means we have a couple more steps than a normal `TCP` or `WebSocket` flow: **Client -> Server** 1. Client publishes on a namespace > `client > pub('foo.bar', { my: 'message' })` 2. The namespace and message get split into an flat array, where the last item is the message > `client > outbound message [ 'foo', 'bar', { my: 'message' } ]` 3. Payload gets encoded by the client > `client > encode(array) // ` 4. Client sends encoded message to the server > `client > send(payload)` 5. Server receives payload Buffer > `server > on('message', decode)` 6. Server receives and unpacks the payload > `server > decode(payload) // our array` 7. Server reconstructs the array, and emits the message > `server > emit('foo.bar', { my: 'message' })` **Server -> Client(s)** 1. Client sends a subscription message to the server, through its binding > `client > on('foo.*', handler)` 2. Server receives a subscription event from the client > `server > on('_sub') // remember client wants 'foo.*' messages` Time passes... 3. Server sends an event to all clients that subscribed > `server > pub('foo.baz', { my: 'message' }) // matches anyone who sent _sub to foo.*` 4. Encode and write the same way the client does to each socket > `server > each connections send(payload)` 5. Client receives payload, unpacks, and emits it > `client > on('foo.*', fn(data)) // this.event === foo.baz` **Serializer** On both server and client, encoder and decoder to pass messages. The `serializer` consists of the encoding and decoding methods to use against payloads. By default, we use `msgpack5`, but doing something as simple as this will work: ```javascript new clientOrServer({ serializer: { encode: JSON.stringify, decode: JSON.parse } }) ``` ## Dependencies ${dependencies()} ## Contributing Contributions welcome; Please submit all pull requests against the master branch. If your pull request contains patches or features, you should include relevant unit tests. Thanks! ## Authors - [Flynn Joffray](http://github.com/nucleardreamer) - - [Nir Ziv](http://github.com/nirziv) - ## License ${license()}