lib/message.js

const _ = require('lodash')
const msgpack = require('msgpack5')()

/**
 * Message encapsulation and decapsulation
 */
class Message {
  /**
   * Creates an instance of Message.
   * @constructor
   * @param {Object} [options] options object
   * @param {Function} [options.encode=msgpack5/encode] Encoder function
   * @param {Function} [options.decode=msgpack5/decode] Decoder function
   */
  constructor ({ encode, decode } = msgpack) {
    /**
     * @prop {Function} encoder function
     */
    this.encoder = encode
    /**
     * @prop {Function} decoder function
     */
    this.decoder = decode
    /**
     * @prop {String} Namespace delimiter
     */
    this.delimiter = '.'
  }

  static delimiter () {
    return this.delimeter || '.'
  }

  /**
   * Pack a message
   * @param {String} namespace namespace of the message
   * @param {any} payload payload of the message
   * @returns {Buffer} Encoded namespace and payload
   */
  pack (namespace, payload) {
    let ret = this.encode(
      [...namespace.split(this.delimiter), payload]
    )
    return ret
  }
  /**
   * @typedef {Object} message
   * @property {String} ns Namespace of the message
   * @property {any} pl Payload of the message
   */

  /**
   * Unpack a message
   * @param {Buffer} data
   * @returns {message}
   * @memberof Message
   */
  unpack (data) {
    let _this = this
    let decoded = _this.decode(data)
    // make sure the message decoded and it's an array
    if (decoded && _.isArray(decoded) && decoded.length) {
      // push null to the unpack if there is no payload
      if (decoded.length === 1) {
        decoded.push(null)
      }
      // namespace consists of all array items minus the last
      let namespace = _.join(_.initial(decoded), '.')
      // payload is the last array item
      let payload = _.last(decoded)
      return { ns: namespace, pl: payload }
    }
  }

  /**
   * Encode data
   * @param {any} data
   * @returns {Buffer}
   * @memberof Message
   */
  encode (data) {
    if (_.isFunction(this.encoder)) {
      try {
        let enc = this.encoder(data)
        return enc
      } catch (err) {
        return err
      }
    } else {
      return data
    }
  }

  /**
   * Decode data
   * @param {Buffer} data
   * @returns {any}
   * @memberof Message
   */
  decode (data) {
    if (_.isFunction(this.decoder)) {
      try {
        return this.decoder(data)
      } catch (err) {
        return err
      }
    } else {
      return data
    }
  }
}

module.exports = Message