Source: lib/transports/index.js

'use strict';
/**
 * Loads and maintains all transports
 * @module skyring/lib/transports
 * @author Eric Satterwhite
 * @tutorial transports
 * @since 1.0.0
 * @requires debug
 * @requires skyring/lib/transports/http
 * @requires skyring/conf
 */

const debug     = require('debug')('skyring:transports')
const Callback  = require('./callback')
const Http      = require('./http')
const toArray   = require('../lang/array/to-array')
const conf      = require('../../conf')
const kLoad     = Symbol('kLoad')
const kShutdown = Symbol.for('kShutdown')
const ENV       = conf.get('node_env')
const defaults  = toArray(conf.get('transport'))

/**
 *
 * @typedef {function} TransportHandler
 * @param {String} method
 * @param {String} uri
 * @param {String} Payload
 * @param {String} id
 * @param {LevelUp} storage A levelup instance container all curring timer data 
 **/

/**
 * @alias module:skyring/lib/transports
 * @constructor
 * @param {TransportHandler|TransportHandler[]|String|String[]} transports Custom transports to register
 *    This can be a Transport class or a
 * @example const path = require('path')
const Skyring = require('skyring')
const kType = Symbol.for('SkyringTransport')

class Fizzbuzz extends Skyring.Transport {
  constructor(opts) {
    super(opts)
    this.name = 'fizzbuzz'
  }
  exec (method, uri, payload, id, timer_store) {
   // send payload to uri...
   timer_store.success(id)
  }
  shutdown(cb) {
    // drain connections...
    // free up event loop
    cb()
  }

  static [Symbol.hasInstance](instance) {
    return instance[kType] === 'fizzbuzztransport'
  }
  get [Symbol.toStringTag]() {
    return 'FizzbuzzTransport'
  }

  get [kType]() {
    return 'fizzbuzztransport'
  }
}


const server = new Skyring({
  transports: [
    'my-transport-module'
  , Fizzbuzz
  , path.resolve(__dirname, '../transports/fake-transport')
  ]
})
 * @example const {Transports, Transport} = require('skyring')
class Fizzbuzz extends Transport {
  constructor(opts) {
    super(opts)
    this.name = 'fizzbuzz'
  }

  exec (method, uri, payload, id, timer_store) {
   // send payload to uri...
   timer_store.remove(id)
  }

  shutdown(cb) {
    // drain connections...
    // free up event loop
    cb()
  }
}

const t = new Transports([
  'my-transport-module'
, Fizzbuz
, path.resolve(__dirname, '../transports/fake-transport')
])
 **/
module.exports = class Transports extends Map {
  constructor(transports) {
    super()
    /**
     * Primary http transport
     * @memberof module:skyring/lib/transports
     * @property {Object} http The default HTTP transport
     **/
    this.set(Http.name.toLowerCase(),  new Http())
    if(ENV === 'test') {
      this.set('callback', new Callback())
    }
    this[kLoad](toArray(transports))
  }

  [kLoad](paths) {
    const transports = new Set(defaults.concat(toArray(paths)))
    for (const path of transports) {
      const transport = typeof path === 'string' ? require(path) : path
      if (typeof transport !== 'function') {
        throw new TypeError('A Transport must export a function')
      }

      if (typeof transport.prototype.exec !== 'function') {
        throw new TypeError('A Transport must have an "exec" function')
      }

      if (transport.prototype.exec.length !== 5) {
        throw new Error('Transports must accept five parameters')
      }

      if (typeof transport.name !== 'string' || transport.name.length <= 0) {
        throw new TypeError('transports.name is required and must be a string')
      }

      const name = transport.name.toLowerCase()
      if (this.has(name)) {
        const error = new Error(`A transport with name ${name} is already defined`)
        error.name = 'EEXIST'
        throw error
      }

      debug('loading %s transport', name)
      const instance = new transport(
        conf.get(`transports:${name}`)
      )

      this.set(name, instance)
    }
  }

  [kShutdown](cb) {
    const keys = Array.from(this.values())
    const run = () => {
      if (!keys.length) return cb()
      const transport = keys.pop()
      if (typeof transport.shutdown === 'function') {
        debug(`shutdown ${transport.name} transport`)
        return transport.shutdown(run)
      }
      run()
    }
    run()
  }
}