'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() } }