<commit_before>/* global FileList */

module.exports = WebTorrent

var Buffer = require('safe-buffer').Buffer
var concat = require('simple-concat')
var createTorrent = require('create-torrent')
var debug = require('debug')('webtorrent')
var DHT = require('bittorrent-dht/client') // browser exclude
var EventEmitter = require('events').EventEmitter
var extend = require('xtend')
var inherits = require('inherits')
var loadIPSet = require('load-ip-set') // browser exclude
var parallel = require('run-parallel')
var parseTorrent = require('parse-torrent')
var path = require('path')
var Peer = require('simple-peer')
var randombytes = require('randombytes')
var speedometer = require('speedometer')
var zeroFill = require('zero-fill')

var TCPPool = require('./lib/tcp-pool') // browser exclude
var Torrent = require('./lib/torrent')

/**
 * WebTorrent version.
 */
var VERSION = require('./package.json').version

/**
 * Version number in Azureus-style. Generated from major and minor semver version.
 * For example:
 *   '0.16.1' -> '0016'
 *   '1.2.5' -> '0102'
 */
var VERSION_STR = VERSION.match(/([0-9]+)/g)
  .slice(0, 2)
  .map(function (v) { return zeroFill(2, v) })
  .join('')

/**
 * Version prefix string (used in peer ID). WebTorrent uses the Azureus-style
 * encoding: '-', two characters for client id ('WW'), four ascii digits for version
 * number, '-', followed by random numbers.
 * For example:
 *   '-WW0102-'...
 */
var VERSION_PREFIX = '-WW' + VERSION_STR + '-'

inherits(WebTorrent, EventEmitter)

/**
 * WebTorrent Client
 * @param {Object=} opts
 */
function WebTorrent (opts) {
  var self = this
  if (!(self instanceof WebTorrent)) return new WebTorrent(opts)
  EventEmitter.call(self)

  if (!opts) opts = {}

  if (typeof opts.peerId === 'string') {
    self.peerId = opts.peerId
  } else if (Buffer.isBuffer(opts.peerId)) {
    self.peerId = opts.peerId.toString('hex')
  } else {
    self.peerId = Buffer.from(VERSION_PREFIX + randombytes(9).toString('base64')).toString('hex')
  }
  self.peerIdBuffer = Buffer.from(self.peerId, 'hex')

  if (typeof opts.nodeId === 'string') {
    self.nodeId = opts.nodeId
  } else if (Buffer.isBuffer(opts.nodeId)) {
    self.nodeId = opts.nodeId.toString('hex')
  } else {
    self.nodeId = randombytes(20).toString('hex')
  }
  self.nodeIdBuffer = Buffer.from(self.nodeId, 'hex')

  self.destroyed = false
  self.listening = false
  self.torrentPort = opts.torrentPort || 0
  self.dhtPort = opts.dhtPort || 0
  self.tracker = opts.tracker !== undefined ? opts.tracker : {}
  self.torrents = []
  self.maxConns = Number(opts.maxConns) || 55

  debug(
    'new webtorrent (peerId %s, nodeId %s, port %s)',
    self.peerId, self.nodeId, self.torrentPort
  )

  if (self.tracker) {
    if (typeof self.tracker !== 'object') self.tracker = {}
    if (opts.rtcConfig) {
      // TODO: remove in v1
      console.warn('WebTorrent: opts.rtcConfig is deprecated. Use opts.tracker.rtcConfig instead')
      self.tracker.rtcConfig = opts.rtcConfig
    }
    if (opts.wrtc) {
      // TODO: remove in v1
      console.warn('WebTorrent: opts.wrtc is deprecated. Use opts.tracker.wrtc instead')
      self.tracker.wrtc = opts.wrtc
    }
    if (global.WRTC && !self.tracker.wrtc) self.tracker.wrtc = global.WRTC
    if (typeof self.tracker.wrtc === 'function') self.tracker.wrtc = self.tracker.wrtc() // Initialize
  }

  if (typeof TCPPool === 'function') {
    self._tcpPool = new TCPPool(self)
  } else {
    process.nextTick(function () {
      self._onListening()
    })
  }

  // stats
  self._downloadSpeed = speedometer()
  self._uploadSpeed = speedometer()

  if (opts.dht !== false && typeof DHT === 'function' /* browser exclude */) {
    // use a single DHT instance for all torrents, so the routing table can be reused
    self.dht = new DHT(extend({ nodeId: self.nodeId }, opts.dht))

    self.dht.once('error', function (err) {
      self._destroy(err)
    })

    self.dht.once('listening', function () {
      var address = self.dht.address()
      if (address) self.dhtPort = address.port
    })

    // Ignore warning when there are > 10 torrents in the client
    self.dht.setMaxListeners(0)

    self.dht.listen(self.dhtPort)
  } else {
    self.dht = false
  }

  // Enable or disable BEP19 (Web Seeds). Enabled by default:
  self.enableWebSeeds = opts.webSeeds !== false

  if (typeof loadIPSet === 'function' && opts.blocklist != null) {
    loadIPSet(opts.blocklist, {
      headers: {
        'user-agent': 'WebTorrent/' + VERSION + ' (https://webtorrent.io)'
      }
    }, function (err, ipSet) {
      if (err) return self.error('Failed to load blocklist: ' + err.message)
      self.blocked = ipSet
      ready()
    })
  } else {
    process.nextTick(ready)
  }

  function ready () {
    if (self.destroyed) return
    self.ready = true
    self.emit('ready')
  }
}

WebTorrent.WEBRTC_SUPPORT = Peer.WEBRTC_SUPPORT

Object.defineProperty(WebTorrent.prototype, 'downloadSpeed', {
  get: function () { return this._downloadSpeed() }
})

Object.defineProperty(WebTorrent.prototype, 'uploadSpeed', {
  get: function () { return this._uploadSpeed() }
})

Object.defineProperty(WebTorrent.prototype, 'progress', {
  get: function () {
    var torrents = this.torrents.filter(function (torrent) {
      return torrent.progress !== 1
    })
    var downloaded = torrents.reduce(function (total, torrent) {
      return total + torrent.downloaded
    }, 0)
    var length = torrents.reduce(function (total, torrent) {
      return total + (torrent.length || 0)
    }, 0) || 1
    return downloaded / length
  }
})

Object.defineProperty(WebTorrent.prototype, 'ratio', {
  get: function () {
    var uploaded = this.torrents.reduce(function (total, torrent) {
      return total + torrent.uploaded
    }, 0)
    var received = this.torrents.reduce(function (total, torrent) {
      return total + torrent.received
    }, 0) || 1
    return uploaded / received
  }
})

/**
 * Returns the torrent with the given `torrentId`. Convenience method. Easier than
 * searching through the `client.torrents` array. Returns `null` if no matching torrent
 * found.
 *
 * @param  {string|Buffer|Object|Torrent} torrentId
 * @return {Torrent|null}
 */
WebTorrent.prototype.get = function (torrentId) {
  var self = this
  var i, torrent
  var len = self.torrents.length

  if (torrentId instanceof Torrent) {
    for (i = 0; i < len; i++) {
      torrent = self.torrents[i]
      if (torrent === torrentId) return torrent
    }
  } else {
    var parsed
    try { parsed = parseTorrent(torrentId) } catch (err) {}

    if (!parsed) return null
    if (!parsed.infoHash) throw new Error('Invalid torrent identifier')

    for (i = 0; i < len; i++) {
      torrent = self.torrents[i]
      if (torrent.infoHash === parsed.infoHash) return torrent
    }
  }
  return null
}

// TODO: remove in v1
WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
  console.warn('WebTorrent: client.download() is deprecated. Use client.add() instead')
  return this.add(torrentId, opts, ontorrent)
}

/**
 * Start downloading a new torrent. Aliased as `client.download`.
 * @param {string|Buffer|Object} torrentId
 * @param {Object} opts torrent-specific options
 * @param {function=} ontorrent called when the torrent is ready (has metadata)
 */
WebTorrent.prototype.add = function (torrentId, opts, ontorrent) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.add(torrentId, null, opts)

  debug('add')
  opts = opts ? extend(opts) : {}

  var torrent = new Torrent(torrentId, self, opts)
  self.torrents.push(torrent)

  torrent.once('_infoHash', onInfoHash)
  torrent.once('ready', onReady)
  torrent.once('close', onClose)

  function onInfoHash () {
    if (self.destroyed) return
    for (var i = 0, len = self.torrents.length; i < len; i++) {
      var t = self.torrents[i]
      if (t.infoHash === torrent.infoHash && t !== torrent) {
        torrent._destroy(new Error('Cannot add duplicate torrent ' + torrent.infoHash))
        return
      }
    }
  }

  function onReady () {
    if (self.destroyed) return
    if (typeof ontorrent === 'function') ontorrent(torrent)
    self.emit('torrent', torrent)
  }

  function onClose () {
    torrent.removeListener('_infoHash', onInfoHash)
    torrent.removeListener('ready', onReady)
    torrent.removeListener('close', onClose)
  }

  return torrent
}

/**
 * Start seeding a new file/folder.
 * @param  {string|File|FileList|Buffer|Array.<string|File|Buffer>} input
 * @param  {Object=} opts
 * @param  {function=} onseed called when torrent is seeding
 */
WebTorrent.prototype.seed = function (input, opts, onseed) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.seed(input, null, opts)

  debug('seed')
  opts = opts ? extend(opts) : {}

  // When seeding from fs path, initialize store from that path to avoid a copy
  if (typeof input === 'string') opts.path = path.dirname(input)
  if (!opts.createdBy) opts.createdBy = 'WebTorrent/' + VERSION_STR

  var torrent = self.add(null, opts, onTorrent)
  var streams

  if (isFileList(input)) input = Array.prototype.slice.call(input)
  if (!Array.isArray(input)) input = [ input ]

  parallel(input.map(function (item) {
    return function (cb) {
      if (isReadable(item)) concat(item, cb)
      else cb(null, item)
    }
  }), function (err, input) {
    if (self.destroyed) return
    if (err) return torrent._destroy(err)

    createTorrent.parseInput(input, opts, function (err, files) {
      if (self.destroyed) return
      if (err) return torrent._destroy(err)

      streams = files.map(function (file) {
        return file.getStream
      })

      createTorrent(input, opts, function (err, torrentBuf) {
        if (self.destroyed) return
        if (err) return torrent._destroy(err)

        var existingTorrent = self.get(torrentBuf)
        if (existingTorrent) {
          torrent._destroy(new Error('Cannot add duplicate torrent ' + existingTorrent.infoHash))
        } else {
          torrent._onTorrentId(torrentBuf)
        }
      })
    })
  })

  function onTorrent (torrent) {
    var tasks = [
      function (cb) {
        torrent.load(streams, cb)
      }
    ]
    if (self.dht) {
      tasks.push(function (cb) {
        torrent.once('dhtAnnounce', cb)
      })
    }
    parallel(tasks, function (err) {
      if (self.destroyed) return
      if (err) return torrent._destroy(err)
      _onseed(torrent)
    })
  }

  function _onseed (torrent) {
    debug('on seed')
    if (typeof onseed === 'function') onseed(torrent)
    torrent.emit('seed')
    self.emit('seed', torrent)
  }

  return torrent
}

/**
 * Remove a torrent from the client.
 * @param  {string|Buffer|Torrent}   torrentId
 * @param  {function} cb
 */
WebTorrent.prototype.remove = function (torrentId, cb) {
  debug('remove')
  var torrent = this.get(torrentId)
  if (!torrent) throw new Error('No torrent with id ' + torrentId)
  this._remove(torrentId, cb)
}

WebTorrent.prototype._remove = function (torrentId, cb) {
  var torrent = this.get(torrentId)
  if (!torrent) return
  this.torrents.splice(this.torrents.indexOf(torrent), 1)
  torrent.destroy(cb)
}

WebTorrent.prototype.address = function () {
  if (!this.listening) return null
  return this._tcpPool
    ? this._tcpPool.server.address()
    : { address: '0.0.0.0', family: 'IPv4', port: 0 }
}

/**
 * Destroy the client, including all torrents and connections to peers.
 * @param  {function} cb
 */
WebTorrent.prototype.destroy = function (cb) {
  if (this.destroyed) throw new Error('client already destroyed')
  this._destroy(null, cb)
}

WebTorrent.prototype._destroy = function (err, cb) {
  var self = this
  debug('client destroy')
  self.destroyed = true

  var tasks = self.torrents.map(function (torrent) {
    return function (cb) {
      torrent.destroy(cb)
    }
  })

  if (self._tcpPool) {
    tasks.push(function (cb) {
      self._tcpPool.destroy(cb)
    })
  }

  if (self.dht) {
    tasks.push(function (cb) {
      self.dht.destroy(cb)
    })
  }

  parallel(tasks, cb)

  if (err) self.emit('error', err)

  self.torrents = []
  self._tcpPool = null
  self.dht = null
}

WebTorrent.prototype._onListening = function () {
  this.listening = true

  if (this._tcpPool) {
    // Sometimes server.address() returns `null` in Docker.
    // WebTorrent issue: https://github.com/feross/bittorrent-swarm/pull/18
    var address = this._tcpPool.server.address()
    if (address) this.torrentPort = address.port
  }

  this.emit('listening')
}

/**
 * Check if `obj` is a node Readable stream
 * @param  {*} obj
 * @return {boolean}
 */
function isReadable (obj) {
  return typeof obj === 'object' && obj != null && typeof obj.pipe === 'function'
}

/**
 * Check if `obj` is a W3C `FileList` object
 * @param  {*} obj
 * @return {boolean}
 */
function isFileList (obj) {
  return typeof FileList !== 'undefined' && obj instanceof FileList
}
<commit_msg>Remove lazy-wrtc code (to be moved into bittorrent-tracker)<commit_after>/* global FileList */

module.exports = WebTorrent

var Buffer = require('safe-buffer').Buffer
var concat = require('simple-concat')
var createTorrent = require('create-torrent')
var debug = require('debug')('webtorrent')
var DHT = require('bittorrent-dht/client') // browser exclude
var EventEmitter = require('events').EventEmitter
var extend = require('xtend')
var inherits = require('inherits')
var loadIPSet = require('load-ip-set') // browser exclude
var parallel = require('run-parallel')
var parseTorrent = require('parse-torrent')
var path = require('path')
var Peer = require('simple-peer')
var randombytes = require('randombytes')
var speedometer = require('speedometer')
var zeroFill = require('zero-fill')

var TCPPool = require('./lib/tcp-pool') // browser exclude
var Torrent = require('./lib/torrent')

/**
 * WebTorrent version.
 */
var VERSION = require('./package.json').version

/**
 * Version number in Azureus-style. Generated from major and minor semver version.
 * For example:
 *   '0.16.1' -> '0016'
 *   '1.2.5' -> '0102'
 */
var VERSION_STR = VERSION.match(/([0-9]+)/g)
  .slice(0, 2)
  .map(function (v) { return zeroFill(2, v) })
  .join('')

/**
 * Version prefix string (used in peer ID). WebTorrent uses the Azureus-style
 * encoding: '-', two characters for client id ('WW'), four ascii digits for version
 * number, '-', followed by random numbers.
 * For example:
 *   '-WW0102-'...
 */
var VERSION_PREFIX = '-WW' + VERSION_STR + '-'

inherits(WebTorrent, EventEmitter)

/**
 * WebTorrent Client
 * @param {Object=} opts
 */
function WebTorrent (opts) {
  var self = this
  if (!(self instanceof WebTorrent)) return new WebTorrent(opts)
  EventEmitter.call(self)

  if (!opts) opts = {}

  if (typeof opts.peerId === 'string') {
    self.peerId = opts.peerId
  } else if (Buffer.isBuffer(opts.peerId)) {
    self.peerId = opts.peerId.toString('hex')
  } else {
    self.peerId = Buffer.from(VERSION_PREFIX + randombytes(9).toString('base64')).toString('hex')
  }
  self.peerIdBuffer = Buffer.from(self.peerId, 'hex')

  if (typeof opts.nodeId === 'string') {
    self.nodeId = opts.nodeId
  } else if (Buffer.isBuffer(opts.nodeId)) {
    self.nodeId = opts.nodeId.toString('hex')
  } else {
    self.nodeId = randombytes(20).toString('hex')
  }
  self.nodeIdBuffer = Buffer.from(self.nodeId, 'hex')

  self.destroyed = false
  self.listening = false
  self.torrentPort = opts.torrentPort || 0
  self.dhtPort = opts.dhtPort || 0
  self.tracker = opts.tracker !== undefined ? opts.tracker : {}
  self.torrents = []
  self.maxConns = Number(opts.maxConns) || 55

  debug(
    'new webtorrent (peerId %s, nodeId %s, port %s)',
    self.peerId, self.nodeId, self.torrentPort
  )

  if (self.tracker) {
    if (typeof self.tracker !== 'object') self.tracker = {}
    if (opts.rtcConfig) {
      // TODO: remove in v1
      console.warn('WebTorrent: opts.rtcConfig is deprecated. Use opts.tracker.rtcConfig instead')
      self.tracker.rtcConfig = opts.rtcConfig
    }
    if (opts.wrtc) {
      // TODO: remove in v1
      console.warn('WebTorrent: opts.wrtc is deprecated. Use opts.tracker.wrtc instead')
      self.tracker.wrtc = opts.wrtc
    }
    if (global.WRTC && !self.tracker.wrtc) {
      self.tracker.wrtc = global.WRTC
    }
  }

  if (typeof TCPPool === 'function') {
    self._tcpPool = new TCPPool(self)
  } else {
    process.nextTick(function () {
      self._onListening()
    })
  }

  // stats
  self._downloadSpeed = speedometer()
  self._uploadSpeed = speedometer()

  if (opts.dht !== false && typeof DHT === 'function' /* browser exclude */) {
    // use a single DHT instance for all torrents, so the routing table can be reused
    self.dht = new DHT(extend({ nodeId: self.nodeId }, opts.dht))

    self.dht.once('error', function (err) {
      self._destroy(err)
    })

    self.dht.once('listening', function () {
      var address = self.dht.address()
      if (address) self.dhtPort = address.port
    })

    // Ignore warning when there are > 10 torrents in the client
    self.dht.setMaxListeners(0)

    self.dht.listen(self.dhtPort)
  } else {
    self.dht = false
  }

  // Enable or disable BEP19 (Web Seeds). Enabled by default:
  self.enableWebSeeds = opts.webSeeds !== false

  if (typeof loadIPSet === 'function' && opts.blocklist != null) {
    loadIPSet(opts.blocklist, {
      headers: {
        'user-agent': 'WebTorrent/' + VERSION + ' (https://webtorrent.io)'
      }
    }, function (err, ipSet) {
      if (err) return self.error('Failed to load blocklist: ' + err.message)
      self.blocked = ipSet
      ready()
    })
  } else {
    process.nextTick(ready)
  }

  function ready () {
    if (self.destroyed) return
    self.ready = true
    self.emit('ready')
  }
}

WebTorrent.WEBRTC_SUPPORT = Peer.WEBRTC_SUPPORT

Object.defineProperty(WebTorrent.prototype, 'downloadSpeed', {
  get: function () { return this._downloadSpeed() }
})

Object.defineProperty(WebTorrent.prototype, 'uploadSpeed', {
  get: function () { return this._uploadSpeed() }
})

Object.defineProperty(WebTorrent.prototype, 'progress', {
  get: function () {
    var torrents = this.torrents.filter(function (torrent) {
      return torrent.progress !== 1
    })
    var downloaded = torrents.reduce(function (total, torrent) {
      return total + torrent.downloaded
    }, 0)
    var length = torrents.reduce(function (total, torrent) {
      return total + (torrent.length || 0)
    }, 0) || 1
    return downloaded / length
  }
})

Object.defineProperty(WebTorrent.prototype, 'ratio', {
  get: function () {
    var uploaded = this.torrents.reduce(function (total, torrent) {
      return total + torrent.uploaded
    }, 0)
    var received = this.torrents.reduce(function (total, torrent) {
      return total + torrent.received
    }, 0) || 1
    return uploaded / received
  }
})

/**
 * Returns the torrent with the given `torrentId`. Convenience method. Easier than
 * searching through the `client.torrents` array. Returns `null` if no matching torrent
 * found.
 *
 * @param  {string|Buffer|Object|Torrent} torrentId
 * @return {Torrent|null}
 */
WebTorrent.prototype.get = function (torrentId) {
  var self = this
  var i, torrent
  var len = self.torrents.length

  if (torrentId instanceof Torrent) {
    for (i = 0; i < len; i++) {
      torrent = self.torrents[i]
      if (torrent === torrentId) return torrent
    }
  } else {
    var parsed
    try { parsed = parseTorrent(torrentId) } catch (err) {}

    if (!parsed) return null
    if (!parsed.infoHash) throw new Error('Invalid torrent identifier')

    for (i = 0; i < len; i++) {
      torrent = self.torrents[i]
      if (torrent.infoHash === parsed.infoHash) return torrent
    }
  }
  return null
}

// TODO: remove in v1
WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
  console.warn('WebTorrent: client.download() is deprecated. Use client.add() instead')
  return this.add(torrentId, opts, ontorrent)
}

/**
 * Start downloading a new torrent. Aliased as `client.download`.
 * @param {string|Buffer|Object} torrentId
 * @param {Object} opts torrent-specific options
 * @param {function=} ontorrent called when the torrent is ready (has metadata)
 */
WebTorrent.prototype.add = function (torrentId, opts, ontorrent) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.add(torrentId, null, opts)

  debug('add')
  opts = opts ? extend(opts) : {}

  var torrent = new Torrent(torrentId, self, opts)
  self.torrents.push(torrent)

  torrent.once('_infoHash', onInfoHash)
  torrent.once('ready', onReady)
  torrent.once('close', onClose)

  function onInfoHash () {
    if (self.destroyed) return
    for (var i = 0, len = self.torrents.length; i < len; i++) {
      var t = self.torrents[i]
      if (t.infoHash === torrent.infoHash && t !== torrent) {
        torrent._destroy(new Error('Cannot add duplicate torrent ' + torrent.infoHash))
        return
      }
    }
  }

  function onReady () {
    if (self.destroyed) return
    if (typeof ontorrent === 'function') ontorrent(torrent)
    self.emit('torrent', torrent)
  }

  function onClose () {
    torrent.removeListener('_infoHash', onInfoHash)
    torrent.removeListener('ready', onReady)
    torrent.removeListener('close', onClose)
  }

  return torrent
}

/**
 * Start seeding a new file/folder.
 * @param  {string|File|FileList|Buffer|Array.<string|File|Buffer>} input
 * @param  {Object=} opts
 * @param  {function=} onseed called when torrent is seeding
 */
WebTorrent.prototype.seed = function (input, opts, onseed) {
  var self = this
  if (self.destroyed) throw new Error('client is destroyed')
  if (typeof opts === 'function') return self.seed(input, null, opts)

  debug('seed')
  opts = opts ? extend(opts) : {}

  // When seeding from fs path, initialize store from that path to avoid a copy
  if (typeof input === 'string') opts.path = path.dirname(input)
  if (!opts.createdBy) opts.createdBy = 'WebTorrent/' + VERSION_STR

  var torrent = self.add(null, opts, onTorrent)
  var streams

  if (isFileList(input)) input = Array.prototype.slice.call(input)
  if (!Array.isArray(input)) input = [ input ]

  parallel(input.map(function (item) {
    return function (cb) {
      if (isReadable(item)) concat(item, cb)
      else cb(null, item)
    }
  }), function (err, input) {
    if (self.destroyed) return
    if (err) return torrent._destroy(err)

    createTorrent.parseInput(input, opts, function (err, files) {
      if (self.destroyed) return
      if (err) return torrent._destroy(err)

      streams = files.map(function (file) {
        return file.getStream
      })

      createTorrent(input, opts, function (err, torrentBuf) {
        if (self.destroyed) return
        if (err) return torrent._destroy(err)

        var existingTorrent = self.get(torrentBuf)
        if (existingTorrent) {
          torrent._destroy(new Error('Cannot add duplicate torrent ' + existingTorrent.infoHash))
        } else {
          torrent._onTorrentId(torrentBuf)
        }
      })
    })
  })

  function onTorrent (torrent) {
    var tasks = [
      function (cb) {
        torrent.load(streams, cb)
      }
    ]
    if (self.dht) {
      tasks.push(function (cb) {
        torrent.once('dhtAnnounce', cb)
      })
    }
    parallel(tasks, function (err) {
      if (self.destroyed) return
      if (err) return torrent._destroy(err)
      _onseed(torrent)
    })
  }

  function _onseed (torrent) {
    debug('on seed')
    if (typeof onseed === 'function') onseed(torrent)
    torrent.emit('seed')
    self.emit('seed', torrent)
  }

  return torrent
}

/**
 * Remove a torrent from the client.
 * @param  {string|Buffer|Torrent}   torrentId
 * @param  {function} cb
 */
WebTorrent.prototype.remove = function (torrentId, cb) {
  debug('remove')
  var torrent = this.get(torrentId)
  if (!torrent) throw new Error('No torrent with id ' + torrentId)
  this._remove(torrentId, cb)
}

WebTorrent.prototype._remove = function (torrentId, cb) {
  var torrent = this.get(torrentId)
  if (!torrent) return
  this.torrents.splice(this.torrents.indexOf(torrent), 1)
  torrent.destroy(cb)
}

WebTorrent.prototype.address = function () {
  if (!this.listening) return null
  return this._tcpPool
    ? this._tcpPool.server.address()
    : { address: '0.0.0.0', family: 'IPv4', port: 0 }
}

/**
 * Destroy the client, including all torrents and connections to peers.
 * @param  {function} cb
 */
WebTorrent.prototype.destroy = function (cb) {
  if (this.destroyed) throw new Error('client already destroyed')
  this._destroy(null, cb)
}

WebTorrent.prototype._destroy = function (err, cb) {
  var self = this
  debug('client destroy')
  self.destroyed = true

  var tasks = self.torrents.map(function (torrent) {
    return function (cb) {
      torrent.destroy(cb)
    }
  })

  if (self._tcpPool) {
    tasks.push(function (cb) {
      self._tcpPool.destroy(cb)
    })
  }

  if (self.dht) {
    tasks.push(function (cb) {
      self.dht.destroy(cb)
    })
  }

  parallel(tasks, cb)

  if (err) self.emit('error', err)

  self.torrents = []
  self._tcpPool = null
  self.dht = null
}

WebTorrent.prototype._onListening = function () {
  this.listening = true

  if (this._tcpPool) {
    // Sometimes server.address() returns `null` in Docker.
    // WebTorrent issue: https://github.com/feross/bittorrent-swarm/pull/18
    var address = this._tcpPool.server.address()
    if (address) this.torrentPort = address.port
  }

  this.emit('listening')
}

/**
 * Check if `obj` is a node Readable stream
 * @param  {*} obj
 * @return {boolean}
 */
function isReadable (obj) {
  return typeof obj === 'object' && obj != null && typeof obj.pipe === 'function'
}

/**
 * Check if `obj` is a W3C `FileList` object
 * @param  {*} obj
 * @return {boolean}
 */
function isFileList (obj) {
  return typeof FileList !== 'undefined' && obj instanceof FileList
}
<|endoftext|>


<commit_before>var after = require('after-all')
var lexint = require('lexicographic-integer')
var collect = require('stream-collector')
var through = require('through2')
var pump = require('pump')
var from = require('from2')
var mutexify = require('mutexify')
var cuid = require('cuid')
var logs = require('level-logs')
var events = require('events')
var util = require('util')
var enumerate = require('level-enumerate')
var replicate = require('./lib/replicate')
var messages = require('./lib/messages')
var hash = require('./lib/hash')

var ID = '!!id'
var CHANGES = '!changes!'
var NODES = '!nodes!'
var HEADS = '!heads!'

var CHECKSUM_MISMATCH = new Error('Checksum mismatch')
var INVALID_LOG = new Error('Invalid log sequence')

INVALID_LOG.notFound = true
INVALID_LOG.status = 404

var noop = function () {}

var Hyperlog = function (db, opts) {
  if (!(this instanceof Hyperlog)) return new Hyperlog(db, opts)
  if (!opts) opts = {}

  events.EventEmitter.call(this)

  this.id = opts.id || null
  this.enumerate = enumerate(db, {prefix: 'enum'})
  this.db = db
  this.logs = logs(db, {prefix: 'logs', valueEncoding: messages.Entry})
  this.lock = opts.lock || mutexify()
  this.changes = 0
  this.setMaxListeners(0)

  var self = this

  this.lock(function (release) {
    collect(db.createKeyStream({gt: CHANGES, lt: CHANGES + '~', reverse: true, limit: 1}), function (_, keys) {
      self.changes = keys && keys.length ? lexint.unpack(keys[0].split('!').pop(), 'hex') : 0
      if (self.id) return release()
      db.get(ID, {valueEncoding: 'utf-8'}, function (_, id) {
        self.id = id || cuid()
        if (id) return release()
        db.put(ID, self.id, function () {
          release()
        })
      })
    })
  })
}

util.inherits(Hyperlog, events.EventEmitter)

Hyperlog.prototype.ready = function (cb) {
  if (this.id) return cb()
  this.lock(function (release) {
    release()
    cb()
  })
}

Hyperlog.prototype.heads = function (cb) {
  var self = this

  var rs = this.db.createValueStream({
    gt: HEADS,
    lt: HEADS + '~',
    valueEncoding: 'utf-8'
  })

  var format = through.obj(function (key, enc, cb) {
    self.get(key, cb)
  })

  return collect(pump(rs, format), cb)
}

Hyperlog.prototype.get = function (key, cb) {
  this.db.get(NODES + key, {valueEncoding: 'binary'}, function (err, buf) {
    if (err) return cb(err)
    cb(null, messages.Node.decode(buf))
  })
}

var add = function (dag, links, value, opts, cb) {
  var logLinks = []
  var id = opts.log || dag.id

  var next = after(function (err) {
    if (err) return cb(err)

    var onlocked = function (release) {
      dag.logs.head(id, function (err, seq) {
        if (err) return release(cb, err)

        var node = {
          change: dag.changes + 1,
          log: id,
          seq: seq + 1,
          key: hash(links, value),
          value: value,
          links: links
        }

        if (opts.hash && node.key !== opts.hash) return release(cb, CHECKSUM_MISMATCH)
        if (opts.seq && node.seq !== opts.seq) return release(cb, INVALID_LOG)

        var log = {
          change: dag.changes + 1,
          node: node.key,
          links: logLinks
        }

        var onclone = function (clone) {
          if (!opts.log) return release(cb, null, clone)
          dag.db.put(dag.logs.key(node.log, node.seq), messages.Entry.encode(log), function (err) {
            if (err) return release(cb, err)
            release(cb, null, clone)
          })
        }

        dag.get(node.key, function (_, clone) {
          if (clone) return onclone(clone)

          var batch = []
          for (var i = 0; i < links.length; i++) batch.push({type: 'del', key: HEADS + links[i]})
          batch.push({type: 'put', key: CHANGES + lexint.pack(node.change, 'hex'), value: node.key})
          batch.push({type: 'put', key: NODES + node.key, value: messages.Node.encode(node)})
          batch.push({type: 'put', key: HEADS + node.key, value: node.key})
          batch.push({type: 'put', key: dag.logs.key(node.log, node.seq), value: messages.Entry.encode(log)})

          dag.db.batch(batch, function (err) {
            if (err) return release(cb, err)
            dag.changes = node.change
            dag.emit('add')
            release(cb, null, node)
          })
        })
      })
    }

    if (opts.release) return onlocked(opts.release)

    dag.lock(onlocked)
  })

  var nextLink = function () {
    var cb = next()
    return function (err, link) {
      if (err) return cb(err)
      if (link.log !== id && logLinks.indexOf(link.log) === -1) logLinks.push(link.log)
      cb(null)
    }
  }

  for (var i = 0; i < links.length; i++) {
    if (typeof links[i] !== 'string') links[i] = links[i].key
    dag.get(links[i], nextLink())
  }
}

var createLiveStream = function (dag, opts) {
  var since = opts.since || 0
  var wait = null

  var read = function (size, cb) {
    if (dag.changes <= since) {
      wait = cb
      return
    }

    dag.db.get(CHANGES + lexint.pack(since + 1, 'hex'), function (err, hash) {
      if (err) return cb(err)
      dag.get(hash, function (err, node) {
        if (err) return cb(err)
        since = node.change
        cb(null, node)
      })
    })
  }

  var kick = function () {
    if (!wait) return
    var cb = wait
    wait = null
    read(0, cb)
  }

  dag.on('add', kick)
  dag.ready(kick)

  var rs = from.obj(read)

  rs.once('close', function () {
    dag.removeListener('add', kick)
  })

  return rs
}

Hyperlog.prototype.createReadStream = function (opts) {
  if (!opts) opts = {}
  if (opts.live) return createLiveStream(this, opts)

  var self = this
  var since = opts.since || 0
  var until = opts.until || 0

  var keys = this.db.createValueStream({
    gt: CHANGES + lexint.pack(since, 'hex'),
    lt: CHANGES + (until ? lexint.pack(until, 'hex') : '~'),
    valueEncoding: 'utf-8',
    reverse: opts.reverse
  })

  var get = function (key, enc, cb) {
    self.get(key, cb)
  }

  return pump(keys, through.obj(get))
}

Hyperlog.prototype.replicate =
Hyperlog.prototype.createReplicationStream = function (opts) {
  return replicate(this, opts)
}

Hyperlog.prototype.get = function (key, cb) {
  this.db.get(NODES + key, {valueEncoding: messages.Node}, cb)
}

Hyperlog.prototype.add = function (links, value, opts, cb) {
  if (typeof opts === 'function') return this.add(links, value, null, opts)
  if (!cb) cb = noop
  if (!opts) opts = {}
  if (!links) links = []
  if (!Array.isArray(links)) links = [links]
  if (typeof value === 'string') value = new Buffer(value)

  var self = this
  this.ready(function () {
    add(self, links, value, opts, cb)
  })
}

Hyperlog.prototype.append = function (value, cb) {
  if (!cb) cb = noop
  var self = this
  this.lock(function (release) {
    self.heads(function (err, heads) {
      if (err) return release(cb, err)
      add(self, heads, value, {release: release}, cb)
    })
  })
}

module.exports = Hyperlog
<commit_msg>ensure monotone changes in ready method<commit_after>var after = require('after-all')
var lexint = require('lexicographic-integer')
var collect = require('stream-collector')
var through = require('through2')
var pump = require('pump')
var from = require('from2')
var mutexify = require('mutexify')
var cuid = require('cuid')
var logs = require('level-logs')
var events = require('events')
var util = require('util')
var enumerate = require('level-enumerate')
var replicate = require('./lib/replicate')
var messages = require('./lib/messages')
var hash = require('./lib/hash')

var ID = '!!id'
var CHANGES = '!changes!'
var NODES = '!nodes!'
var HEADS = '!heads!'

var CHECKSUM_MISMATCH = new Error('Checksum mismatch')
var INVALID_LOG = new Error('Invalid log sequence')

INVALID_LOG.notFound = true
INVALID_LOG.status = 404

var noop = function () {}

var Hyperlog = function (db, opts) {
  if (!(this instanceof Hyperlog)) return new Hyperlog(db, opts)
  if (!opts) opts = {}

  events.EventEmitter.call(this)

  this.id = opts.id || null
  this.enumerate = enumerate(db, {prefix: 'enum'})
  this.db = db
  this.logs = logs(db, {prefix: 'logs', valueEncoding: messages.Entry})
  this.lock = opts.lock || mutexify()
  this.changes = 0
  this.setMaxListeners(0)

  var self = this

  this.lock(function (release) {
    collect(db.createKeyStream({gt: CHANGES, lt: CHANGES + '~', reverse: true, limit: 1}), function (_, keys) {
      self.changes = Math.max(self.changes, keys && keys.length ? lexint.unpack(keys[0].split('!').pop(), 'hex') : 0)
      if (self.id) return release()
      db.get(ID, {valueEncoding: 'utf-8'}, function (_, id) {
        self.id = id || cuid()
        if (id) return release()
        db.put(ID, self.id, function () {
          release()
        })
      })
    })
  })
}

util.inherits(Hyperlog, events.EventEmitter)

Hyperlog.prototype.ready = function (cb) {
  if (this.id) return cb()
  this.lock(function (release) {
    release()
    cb()
  })
}

Hyperlog.prototype.heads = function (cb) {
  var self = this

  var rs = this.db.createValueStream({
    gt: HEADS,
    lt: HEADS + '~',
    valueEncoding: 'utf-8'
  })

  var format = through.obj(function (key, enc, cb) {
    self.get(key, cb)
  })

  return collect(pump(rs, format), cb)
}

Hyperlog.prototype.get = function (key, cb) {
  this.db.get(NODES + key, {valueEncoding: 'binary'}, function (err, buf) {
    if (err) return cb(err)
    cb(null, messages.Node.decode(buf))
  })
}

var add = function (dag, links, value, opts, cb) {
  var logLinks = []
  var id = opts.log || dag.id

  var next = after(function (err) {
    if (err) return cb(err)

    var onlocked = function (release) {
      dag.logs.head(id, function (err, seq) {
        if (err) return release(cb, err)

        var node = {
          change: dag.changes + 1,
          log: id,
          seq: seq + 1,
          key: hash(links, value),
          value: value,
          links: links
        }

        if (opts.hash && node.key !== opts.hash) return release(cb, CHECKSUM_MISMATCH)
        if (opts.seq && node.seq !== opts.seq) return release(cb, INVALID_LOG)

        var log = {
          change: dag.changes + 1,
          node: node.key,
          links: logLinks
        }

        var onclone = function (clone) {
          if (!opts.log) return release(cb, null, clone)
          dag.db.put(dag.logs.key(node.log, node.seq), messages.Entry.encode(log), function (err) {
            if (err) return release(cb, err)
            release(cb, null, clone)
          })
        }

        dag.get(node.key, function (_, clone) {
          if (clone) return onclone(clone)

          var batch = []
          for (var i = 0; i < links.length; i++) batch.push({type: 'del', key: HEADS + links[i]})
          batch.push({type: 'put', key: CHANGES + lexint.pack(node.change, 'hex'), value: node.key})
          batch.push({type: 'put', key: NODES + node.key, value: messages.Node.encode(node)})
          batch.push({type: 'put', key: HEADS + node.key, value: node.key})
          batch.push({type: 'put', key: dag.logs.key(node.log, node.seq), value: messages.Entry.encode(log)})

          dag.db.batch(batch, function (err) {
            if (err) return release(cb, err)
            dag.changes = node.change
            dag.emit('add')
            release(cb, null, node)
          })
        })
      })
    }

    if (opts.release) return onlocked(opts.release)

    dag.lock(onlocked)
  })

  var nextLink = function () {
    var cb = next()
    return function (err, link) {
      if (err) return cb(err)
      if (link.log !== id && logLinks.indexOf(link.log) === -1) logLinks.push(link.log)
      cb(null)
    }
  }

  for (var i = 0; i < links.length; i++) {
    if (typeof links[i] !== 'string') links[i] = links[i].key
    dag.get(links[i], nextLink())
  }
}

var createLiveStream = function (dag, opts) {
  var since = opts.since || 0
  var wait = null

  var read = function (size, cb) {
    if (dag.changes <= since) {
      wait = cb
      return
    }

    dag.db.get(CHANGES + lexint.pack(since + 1, 'hex'), function (err, hash) {
      if (err) return cb(err)
      dag.get(hash, function (err, node) {
        if (err) return cb(err)
        since = node.change
        cb(null, node)
      })
    })
  }

  var kick = function () {
    if (!wait) return
    var cb = wait
    wait = null
    read(0, cb)
  }

  dag.on('add', kick)
  dag.ready(kick)

  var rs = from.obj(read)

  rs.once('close', function () {
    dag.removeListener('add', kick)
  })

  return rs
}

Hyperlog.prototype.createReadStream = function (opts) {
  if (!opts) opts = {}
  if (opts.live) return createLiveStream(this, opts)

  var self = this
  var since = opts.since || 0
  var until = opts.until || 0

  var keys = this.db.createValueStream({
    gt: CHANGES + lexint.pack(since, 'hex'),
    lt: CHANGES + (until ? lexint.pack(until, 'hex') : '~'),
    valueEncoding: 'utf-8',
    reverse: opts.reverse
  })

  var get = function (key, enc, cb) {
    self.get(key, cb)
  }

  return pump(keys, through.obj(get))
}

Hyperlog.prototype.replicate =
Hyperlog.prototype.createReplicationStream = function (opts) {
  return replicate(this, opts)
}

Hyperlog.prototype.get = function (key, cb) {
  this.db.get(NODES + key, {valueEncoding: messages.Node}, cb)
}

Hyperlog.prototype.add = function (links, value, opts, cb) {
  if (typeof opts === 'function') return this.add(links, value, null, opts)
  if (!cb) cb = noop
  if (!opts) opts = {}
  if (!links) links = []
  if (!Array.isArray(links)) links = [links]
  if (typeof value === 'string') value = new Buffer(value)

  var self = this
  this.ready(function () {
    add(self, links, value, opts, cb)
  })
}

Hyperlog.prototype.append = function (value, cb) {
  if (!cb) cb = noop
  var self = this
  this.lock(function (release) {
    self.heads(function (err, heads) {
      if (err) return release(cb, err)
      add(self, heads, value, {release: release}, cb)
    })
  })
}

module.exports = Hyperlog
<|endoftext|>


<commit_before>var concat = require('concat-stream')
var http = require('http')
var fs = require('fs')
var prStatus = require('./prcheck.js')
var collab = require('./collab.js')

module.exports = function(onHook) {
  var server = http.createServer(handler)

  function handler(req, res) {
    console.log(req.method, req.url)
    if (req.method === 'POST' && req.url === '/push') {
      return handleHook(req, res)
    }

    if (req.method === 'POST' && req.url === '/pr') {
      return prStatus(function(err, issues) {
        checkPR(res, err, issues)
      })
    }

    if (req.method === 'POST' && req.url === '/collab') {
      return collab(function(err, issues) {
        checkCollab(res, err, userRepos)
      })
    }

    res.statusCode = 404
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify({
      error: 404,
      message: 'not_found',
      hints: 'POST to /push'
    }, true, 2))
  }

  function handleHook(req, res) {
    req.pipe(concat(function(buff) {
      var hookObj = JSON.parse(buff)
      // fs.writeFileSync('email.json', JSON.stringify(hookObj))
      if (onHook) onHook(hookObj, req)
    }))
  }

  function checkPR(res, err, issues) {
    if (err) console.log(err)
    res.statusCode = 200
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify({
      error: 200,
      issuesCount: issues.length
    }, true, 2))
    console.log("i did it!", issues.length)
  }

  function checkCollab(res, err, userRepos) {
    if (err) console.log(err)
    res.statusCode = 200
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify({
      error: 200
    }, true, 2))
    console.log("i did it!", userRepos)
  }

  return server
}
<commit_msg>space<commit_after>var concat = require('concat-stream')
var http = require('http')
var fs = require('fs')

var prStatus = require('./prcheck.js')
var collab = require('./collab.js')

module.exports = function(onHook) {
  var server = http.createServer(handler)

  function handler(req, res) {
    console.log(req.method, req.url)
    if (req.method === 'POST' && req.url === '/push') {
      return handleHook(req, res)
    }

    if (req.method === 'POST' && req.url === '/pr') {
      return prStatus(function(err, issues) {
        checkPR(res, err, issues)
      })
    }

    if (req.method === 'POST' && req.url === '/collab') {
      return collab(function(err, issues) {
        checkCollab(res, err, userRepos)
      })
    }

    res.statusCode = 404
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify({
      error: 404,
      message: 'not_found',
      hints: 'POST to /push'
    }, true, 2))
  }

  function handleHook(req, res) {
    req.pipe(concat(function(buff) {
      var hookObj = JSON.parse(buff)
      // fs.writeFileSync('email.json', JSON.stringify(hookObj))
      if (onHook) onHook(hookObj, req)
    }))
  }

  function checkPR(res, err, issues) {
    if (err) console.log(err)
    res.statusCode = 200
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify({
      error: 200,
      issuesCount: issues.length
    }, true, 2))
    console.log("i did it!", issues.length)
  }

  function checkCollab(res, err, userRepos) {
    if (err) console.log(err)
    res.statusCode = 200
    res.setHeader('content-type', 'application/json')
    res.end(JSON.stringify({
      error: 200
    }, true, 2))
    console.log("i did it!", userRepos)
  }

  return server
}
<|endoftext|>


<commit_before>/**
  MailerBot - Client

  Provides different jobs for sending email with invoice data, etc.

  (c) 2013 OptimalBits - MIT Licensed.
*/
"use strict";

var Queue = require('bull');

var JOB_QUEUE_NAME = 'MailerBot';

function MailerBotClient(opts){
  console.debug("Created mailerbot client with opts: %s", opts);
  console.debug(Error().stack);
  this.clientId = opts.clientId || 'default';
  this.jobQueue = Queue(JOB_QUEUE_NAME, opts.redisPort, opts.redisHost);
  this.resultsQueueName = 'MailerBot-client-'+this.clientId;
  this.resultsQueue = Queue(this.resultsQueueName);
}

/**
  Places a request to send an email with optional pdf attachment.

  opts = {
    sender: 'economy@optimalbits.com',
    user: {
      name: '',
      email: ''
    },
    attachment: '',
    attachmentName: 'invoice January',
    subject: 'subject',
    html: '',
    text: 'plain text'
  }
*/
MailerBotClient.prototype.send = function(opts){
  return this.jobQueue.add(opts);
}

module.exports = MailerBotClient;

<commit_msg>improved output of debug logs<commit_after>/**
  MailerBot - Client

  Provides different jobs for sending email with invoice data, etc.

  (c) 2013 OptimalBits - MIT Licensed.
*/
"use strict";

var Queue = require('bull');
var util = require('util');

var JOB_QUEUE_NAME = 'MailerBot';

function MailerBotClient(opts){
  console.debug("Created mailerbot client with opts: %s", util.inspect(opts));
  console.debug(Error().stack);
  this.clientId = opts.clientId || 'default';
  this.jobQueue = Queue(JOB_QUEUE_NAME, opts.redisPort, opts.redisHost);
  this.resultsQueueName = 'MailerBot-client-'+this.clientId;
  this.resultsQueue = Queue(this.resultsQueueName);
}

/**
  Places a request to send an email with optional pdf attachment.

  opts = {
    sender: 'economy@optimalbits.com',
    user: {
      name: '',
      email: ''
    },
    attachment: '',
    attachmentName: 'invoice January',
    subject: 'subject',
    html: '',
    text: 'plain text'
  }
*/
MailerBotClient.prototype.send = function(opts){
  return this.jobQueue.add(opts);
}

module.exports = MailerBotClient;

<|endoftext|>


<commit_before>require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const login = require('./login.js');
const getPullRequests = require('./getPullRequests.js');
const Maybe = require('folktale/maybe');
const app = express();
app.use(bodyParser.json());
app.use(cookieParser());

app.post('/api/login', (req, res) => {
    const {username, password, server} = req.body;
    const loginObj = {
        username,
        password,
        server
    };
    login(loginObj).then(() => {
        res.cookie('login', loginObj, {
            maxAge: 900000,
            httpOnly: false
        });
        res.sendStatus(200);
    }, (error) => {
        res.sendStatus(error.statusCode);
    });
});

app.post('/api/logout', (req, res) => {
    res.clearCookie('login');
    res.sendStatus(200);
});

app.get('/api/pullrequests', (req, res) => {
    Maybe.fromNullable(req.cookies.login).matchWith({
        Just: ({value}) => {
            getPullRequests(value).then(data => res.json(data), error => {
                console.log('index.js', '40', '', error);
                res.sendStatus(500);
            });
        },
        Nothing: () => res.sendStatus(401)
    });
});

app.set('port', process.env.PORT);
app.listen(app.get('port'));
<commit_msg>Set cookie to http only<commit_after>require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const login = require('./login.js');
const getPullRequests = require('./getPullRequests.js');
const Maybe = require('folktale/maybe');
const app = express();
app.use(bodyParser.json());
app.use(cookieParser());

app.post('/api/login', (req, res) => {
    const {username, password, server} = req.body;
    const loginObj = {
        username,
        password,
        server
    };
    login(loginObj).then(() => {
        res.cookie('login', loginObj, {
            httpOnly: true
        });
        res.sendStatus(200);
    }, (error) => {
        res.sendStatus(error.statusCode);
    });
});

app.post('/api/logout', (req, res) => {
    res.clearCookie('login');
    res.sendStatus(200);
});

app.get('/api/pullrequests', (req, res) => {
    Maybe.fromNullable(req.cookies.login).matchWith({
        Just: ({value}) => {
            getPullRequests(value).then(data => res.json(data), error => {
                console.log('index.js', '40', '', error);
                res.sendStatus(500);
            });
        },
        Nothing: () => res.sendStatus(401)
    });
});

app.set('port', process.env.PORT);
app.listen(app.get('port'));
<|endoftext|>


<commit_before>console.time('init')

var AWS = require('aws-sdk')
var Promise = require('bluebird')
var exec = require('./lib/exec')
var fs = Promise.promisifyAll(require('fs'))
var s3 = Promise.promisifyAll(new AWS.S3())
const BUCKET = 'lilybin-scores'
const LY_DIR = process.env.LAMBDA_TASK_ROOT + '/ly'

process.chdir('/tmp')
process.env.PATH += ':' + LY_DIR + '/usr/bin'
process.env.LD_LIBRARY_PATH = LY_DIR + '/usr/lib'

var mime = {
  'pdf' : 'application/octet-stream'
, 'midi': 'application/octet-stream'
, 'png' : 'image/png'
}

exports.handler = function(event, context) {
  console.log('Received event:', JSON.stringify(event, null, 2))
  makeTime('init', 'writing input')

  return fs.writeFileAsync('input.ly', event.body).bind({
    key : event.key
  }).then(makeTime.bind(null, 'writing input', 'lilypond'))
  .then(exec.bind(
    null,
    'lilypond --formats=pdf,png -o rendered input.ly'
  ))
  .then(function (res) {
    this.result = res
  }).then(makeTime.bind(null, 'lilypond', 'upload'))
  .then(function () {
    return uploadFiles(this.key, this.result)
  }).then(console.timeEnd.bind(console, 'upload'))
  .then(function () {
    context.succeed(this.result)
  }).catch(context.fail.bind(context))
}

function makeTime (end, begin) {
  console.timeEnd(end)
  console.time(begin)
}

function uploadFile (key, file, mode) {
  return fs.readFileAsync(file)
  .then(function (data) {
    return s3.putObjectAsync({
      Bucket      : BUCKET
    , Key         : key
    , Body        : data
    , ContentType : mime[mode]
    , StorageClass: 'REDUCED_REDUNDANCY'
    })
  }, noop)                              // ignore error
}

function uploadFiles (key, res) {
  var pdfPromise = uploadFile(key + '.pdf', 'rendered.pdf', 'pdf')
  var midiPromise = uploadFile(key + '.midi', 'rendered.midi', 'midi')
  var pngPromise = (fs.accessAsync || fs.statAsync)('rendered.png')
    .then(function () {
      res.pages = 1
      return uploadFile(key + '-page1.png', 'rendered.png', 'png')
    }, function () {
      return countPages().then(function (pages) {
        res.pages = pages
        var promises = [];

        for (var i = 1; i <= pages; i ++) {
          promises.push(uploadFile(
            key + '-page' + i + '.png', 'rendered-page' + i + '.png', 'png'
          ))
        }
        return Promise.all(promises)
      })
    })

  return Promise.join(pdfPromise, midiPromise, pngPromise)
}

function countPages () {
  var re = new RegExp('rendered-page.*\.png')
  return fs.readdirAsync('.').then(function (files) {
    return files.filter(re.test.bind(re)).length
  })
}

function noop () {}
<commit_msg>Fix MIME-types<commit_after>console.time('init')

var AWS = require('aws-sdk')
var Promise = require('bluebird')
var exec = require('./lib/exec')
var fs = Promise.promisifyAll(require('fs'))
var s3 = Promise.promisifyAll(new AWS.S3())
const BUCKET = 'lilybin-scores'
const LY_DIR = process.env.LAMBDA_TASK_ROOT + '/ly'

process.chdir('/tmp')
process.env.PATH += ':' + LY_DIR + '/usr/bin'
process.env.LD_LIBRARY_PATH = LY_DIR + '/usr/lib'

var mime = {
  'pdf' : 'application/pdf'
, 'midi': 'audio/midi'
, 'png' : 'image/png'
}

exports.handler = function(event, context) {
  console.log('Received event:', JSON.stringify(event, null, 2))
  makeTime('init', 'writing input')

  return fs.writeFileAsync('input.ly', event.body).bind({
    key : event.key
  }).then(makeTime.bind(null, 'writing input', 'lilypond'))
  .then(exec.bind(
    null,
    'lilypond --formats=pdf,png -o rendered input.ly'
  ))
  .then(function (res) {
    this.result = res
  }).then(makeTime.bind(null, 'lilypond', 'upload'))
  .then(function () {
    return uploadFiles(this.key, this.result)
  }).then(console.timeEnd.bind(console, 'upload'))
  .then(function () {
    context.succeed(this.result)
  }).catch(context.fail.bind(context))
}

function makeTime (end, begin) {
  console.timeEnd(end)
  console.time(begin)
}

function uploadFile (key, file, mode) {
  return fs.readFileAsync(file)
  .then(function (data) {
    return s3.putObjectAsync({
      Bucket      : BUCKET
    , Key         : key
    , Body        : data
    , ContentType : mime[mode]
    , StorageClass: 'REDUCED_REDUNDANCY'
    })
  }, noop)                              // ignore error
}

function uploadFiles (key, res) {
  var pdfPromise = uploadFile(key + '.pdf', 'rendered.pdf', 'pdf')
  var midiPromise = uploadFile(key + '.midi', 'rendered.midi', 'midi')
  var pngPromise = (fs.accessAsync || fs.statAsync)('rendered.png')
    .then(function () {
      res.pages = 1
      return uploadFile(key + '-page1.png', 'rendered.png', 'png')
    }, function () {
      return countPages().then(function (pages) {
        res.pages = pages
        var promises = [];

        for (var i = 1; i <= pages; i ++) {
          promises.push(uploadFile(
            key + '-page' + i + '.png', 'rendered-page' + i + '.png', 'png'
          ))
        }
        return Promise.all(promises)
      })
    })

  return Promise.join(pdfPromise, midiPromise, pngPromise)
}

function countPages () {
  var re = new RegExp('rendered-page.*\.png')
  return fs.readdirAsync('.').then(function (files) {
    return files.filter(re.test.bind(re)).length
  })
}

function noop () {}
<|endoftext|>


<commit_before>/**
 * gulp-tannwestlake
 * @param Gulp gulp
 */
module.exports = (function(gulp) {

    // Include our dependencies, starting with style stuff
    var sass        = require('gulp-sass'),
        autoprefix  = require('gulp-autoprefixer'),
        minify      = require('gulp-minify-css'),
        rename      = require('gulp-rename'),

        // Scripts
        uglify      = require('gulp-uglify'),
        strip       = require('gulp-strip-debug'),
        concat      = require('gulp-concat'),

        // Images
        png         = require('imagemin-optipng'),
        jpg         = require('imagemin-jpegtran'),
        gif         = require('imagemin-gifsicle'),
        cache       = require('gulp-cache'),

        // Other
        util        = require('gulp-util'),
        del         = require('del'),
        bust        = require('gulp-buster'),
        notify      = require('gulp-notify'),
        notifier    = require('node-notifier'),
        merge       = require('merge-stream')
        sequence    = require('run-sequence');

    /**
     * Deployment task
     *
     * @param  Object options
     * @return Function
     */
    function deploy(options)
    {
        return function() {
            gulp.src(options.files, {base: '.'}).pipe(gulp.dest(options.destination));
        };
    };

    /**
     * Styles task
     *
     * @param  Object options
     * @return Function
     */
    function styles(options)
    {
        return function() {
            gulp.src(options.src)
                .pipe(sass(options.sass))
                .on('error', util.log)
                .pipe(autoprefix(options.autoprefix))
                .pipe(gulp.dest(options.public))
                .pipe(bust())
                .pipe(gulp.dest(options.cache));
        };
    };

    // Give access to the tasks
    return {
        styles: styles,
        deploy: deploy
    };

});
<commit_msg>:bug: Handle default options for deploy task<commit_after>/**
 * gulp-tannwestlake
 * @param Gulp gulp
 */
module.exports = (function(gulp) {

    // Include our dependencies, starting with style stuff
    var sass        = require('gulp-sass'),
        autoprefix  = require('gulp-autoprefixer'),
        minify      = require('gulp-minify-css'),
        rename      = require('gulp-rename'),

        // Scripts
        uglify      = require('gulp-uglify'),
        strip       = require('gulp-strip-debug'),
        concat      = require('gulp-concat'),

        // Images
        png         = require('imagemin-optipng'),
        jpg         = require('imagemin-jpegtran'),
        gif         = require('imagemin-gifsicle'),
        cache       = require('gulp-cache'),

        // Other
        util        = require('gulp-util'),
        del         = require('del'),
        bust        = require('gulp-buster'),
        notify      = require('gulp-notify'),
        notifier    = require('node-notifier'),
        merge       = require('merge-stream')
        sequence    = require('run-sequence');

    /**
     * Deployment task
     *
     * @param  Object options
     * @return Function
     */
    function deploy(options)
    {
        var defaults = {
            files: ['**/*'],
            destination: '_deploy'
        };

        if(options === undefined) {
            options = defaults;
        } else {

            if(options.files === undefined) {
                options.files = defaults.files;
            }

            if(options.destination === undefined) {
                options.destination = defaults.destination;
            }
        }

        return function() {
            gulp.src(options.files, {base: '.'}).pipe(gulp.dest(options.destination));
        };
    };

    /**
     * Styles task
     *
     * @param  Object options
     * @return Function
     */
    function styles(options)
    {
        return function() {
            gulp.src(options.src)
                .pipe(sass(options.sass))
                .on('error', util.log)
                .pipe(autoprefix(options.autoprefix))
                .pipe(gulp.dest(options.public))
                .pipe(bust())
                .pipe(gulp.dest(options.cache));
        };
    };

    // Give access to the tasks
    return {
        styles: styles,
        deploy: deploy
    };

});
<|endoftext|>


<commit_before>/**
 * Dependencie(s)
 */

const compile = require('./lib/compile')
const { join, extname } = require('path')
const query = require('qs').parse
const parse = require('url').parse
const morph = require('morph-stream')
const body = require('request-body')
const lookup = require('mime-types').contentType
const stream = require('stream')

/**
 * Create web resource.
 *
 * @param {Object} obj
 * @return {Function}
 * @api public
 */

module.exports = (obj, relative = '') => {
  return compile((core, services, req, res) => {
    const method = req.method.toLowerCase()
    const url = parse(join('/', req.url.substring(relative.length)))
    const service = core.has(method, url.pathname)
    if (service) {
      const conf = services[method][service.path]
      return morph(
        data(query(url.query), req, conf.limit)
          .then(val => service({...val, ...req.query}, req, res))
          .then(val => {
            res.statusCode = Number(conf.options.status) || 200
            res.setHeader('Content-Type', conf.options.type || mime(val))
            return val
          }, reason => status(res, reason))
      )
    } else {
      return morph(status(res, {
        status: 501,
        message: `method ${method.toUpperCase()} not implemented`
      }))
    }
  }, obj)
}

/**
 * Return MIME type according of a value.
 *
 * @note we could read the .path property of a stream
 * to get the right content type using mime-types
 *
 * @param {String} type
 * @return {String}
 * @api private
 */

function mime (value) {
  if (typeof value == 'object') {
    let type = 'application/json; charset=utf-8'
    if (value instanceof stream.Stream) {
      type = lookup(extname(value.path)) || type
    }
    return type
  }
  return 'text/plain; charset=utf-8'
}

/**
 * Set response error with custom status status code
 * and payload.
 *
 * @param {ServerResponse} res
 * @param {Object} err
 * @return {Promise}
 * @api private
 */

function status (res, err) {
  const code = res.statusCode = Number(err.status) || 400
  res.setHeader('Content-Type', 'application/json; charset=utf-8')
  return Promise.resolve({
    error: {
      status: code,
      message: err.message,
      payload: err.payload || {}
    }
  })
}

/**
 * Return the content of the body and the query parameters
 * as a unified object.
 *
 * @param {Object} params
 * @param {ServerRequest} req
 * @param {Number} limit (default 100kb)
 * @return {Promise}
 * @api private
 */

function data (params, req, limit = 100000) {
  return new Promise(resolve => {
    const length = Number(req.headers['content-length'])
    if (length && length > 0 && length <= limit) {
      resolve(body(req).then(val => {
        return {
          ...params,
          ...val
        }
      }))
    } else {
      resolve({
        ...params
      })
    }
  })
}
<commit_msg>Remove relative url<commit_after>/**
 * Dependencie(s)
 */

const compile = require('./lib/compile')
const { join, extname } = require('path')
const query = require('qs').parse
const parse = require('url').parse
const morph = require('morph-stream')
const body = require('request-body')
const lookup = require('mime-types').contentType
const stream = require('stream')

/**
 * Create web resource.
 *
 * @param {Object} obj
 * @param {Boolean} dev
 * @return {Function}
 * @api public
 */

module.exports = (obj, dev) => {
  return compile((core, services, req, res) => {
    const method = req.method.toLowerCase()
    const url = parse(join('/', req.url))
    const service = core.has(method, url.pathname)
    if (service) {
      const conf = services[method][service.path]
      return morph(
        data(query(url.query), req, conf.limit)
          .then(val => service({...val, ...req.query}, req, res))
          .then(val => {
            res.statusCode = Number(conf.options.status) || 200
            res.setHeader('Content-Type', conf.options.type || mime(val))
            return val
          }, reason => status(res, reason))
      )
    } else {
      return morph(status(res, {
        status: 501,
        message: `method ${method.toUpperCase()} not implemented`
      }))
    }
  }, obj)
}

/**
 * Return MIME type according of a value.
 *
 * @note we could read the .path property of a stream
 * to get the right content type using mime-types
 *
 * @param {String} type
 * @return {String}
 * @api private
 */

function mime (value) {
  if (typeof value == 'object') {
    let type = 'application/json; charset=utf-8'
    if (value instanceof stream.Stream) {
      type = lookup(extname(value.path)) || type
    }
    return type
  }
  return 'text/plain; charset=utf-8'
}

/**
 * Set response error with custom status status code
 * and payload.
 *
 * @param {ServerResponse} res
 * @param {Object} err
 * @return {Promise}
 * @api private
 */

function status (res, err) {
  const code = res.statusCode = Number(err.status) || 400
  res.setHeader('Content-Type', 'application/json; charset=utf-8')
  return Promise.resolve({
    error: {
      status: code,
      message: err.message,
      payload: err.payload || {}
    }
  })
}

/**
 * Return the content of the body and the query parameters
 * as a unified object.
 *
 * @param {Object} params
 * @param {ServerRequest} req
 * @param {Number} limit (default 100kb)
 * @return {Promise}
 * @api private
 */

function data (params, req, limit = 100000) {
  return new Promise(resolve => {
    const length = Number(req.headers['content-length'])
    if (length && length > 0 && length <= limit) {
      resolve(body(req).then(val => {
        return {
          ...params,
          ...val
        }
      }))
    } else {
      resolve({
        ...params
      })
    }
  })
}
<|endoftext|>


<commit_before>'use strict';

var cheerio = require('cheerio');
var request = require('request');

var BASE_URL = 'https://skiplagged.com/';

function Skiplagged() { };

/**
 * Gets flights for a specific date and cities
 */
Skiplagged.prototype.flights = function(to, from, toDate, fromDate) {
	var flightUrl = BASE_URL;
	// to do: standardize date
	flightUrl += 'src?=' +
	from + '&dst=' +
	to + '&when=' +
	fromDate + '&whenBack=' +
	toDate + '&sort=cost';
	//https://skiplagged.com/?src=JFK&dst=ORD&when=2015-02-24&whenBack=2015-02-26&sort=cost
	console.log(flightUrl);
	getFlights(flightUrl);
};

function getFlights(CONSTRUCTED_URL) {
	this.url = CONSTRUCTED_URL;
	request(url, function(error, response, body) {
		if (!error && response.statusCode == 200) {
			var $ = cheerio.load(body);
    		console.log(body)
  		}
	});
}

module.exports = Skiplagged;
<commit_msg>fix unnecessary name<commit_after>'use strict';

var cheerio = require('cheerio');
var request = require('request');

var BASE_URL = 'https://skiplagged.com/';

function Skiplagged() { };

/**
 * Gets flights for a specific date and cities
 */
Skiplagged.prototype.flights = function(to, from, toDate, fromDate) {
	var flightUrl = BASE_URL;
	// to do: standardize date
	flightUrl += 'src?=' +
	from + '&dst=' +
	to + '&when=' +
	fromDate + '&whenBack=' +
	toDate + '&sort=cost';
	//https://skiplagged.com/?src=JFK&dst=ORD&when=2015-02-24&whenBack=2015-02-26&sort=cost
	console.log(flightUrl);
	getFlights(flightUrl);
};

function getFlights(url) {
	this.url = url;
	request(url, function(error, response, body) {
		if (!error && response.statusCode == 200) {
			var $ = cheerio.load(body);
    		console.log(body)
  		}
	});
}

module.exports = Skiplagged;
<|endoftext|>


<commit_before>'use strict';

var React = require('react');
var ReactDOM = require('react-dom');
var ReactGateway = require('react-gateway');
var focusScope = require('a11y-focus-scope');
var focusStore = require('a11y-focus-store');

var applicationElement;

function setApplicationElement() {
  applicationElement = element;
}

function setFocusOn(element) {
  focusStore.storeFocus();
  if (applicationElement) applicationElement.setAttribute('aria-hidden', 'true');
  focusScope.scopeFocus(this.refs.modal);
}

function resetFocus() {
  focusScope.unscopeFocus();
  if (applicationElement) applicationElement.removeAttribute('aria-hidden');
  focusStore.restoreFocus();
}

module.exports = React.createClass({
  displayName: 'ReactModal2',

  statics: {
    setApplicationElement: setApplicationElement
  },

  propTypes: {
    onClose: React.PropTypes.func.isRequired,

    closeOnEsc: React.PropTypes.bool,
    closeOnBackdropClick: React.PropTypes.bool,

    backdropClassName: React.PropTypes.string,
    backdropStyles: React.PropTypes.object,

    modalClassName: React.PropTypes.string,
    modalStyles: React.PropTypes.object
  },

  getDefaultProps: function() {
    return {
      closeOnEsc: true,
      closeOnBackdropClick: true
    };
  },

  componentDidMount: function() {
    setFocusOn(this.refs.modal);
    document.addEventListener('keydown', this.handleDocumentKeydown);
  },

  componentWillUnmount: function() {
    resetFocus();
    document.removeEventListener('keydown', this.handleDocumentKeydown);
  },

  handleDocumentKeydown: function(event) {
    if (this.props.closeOnEsc && event.keyCode === 27) {
      this.props.onClose();
    }
  },

  handleBackdropClick: function() {
    if (this.props.closeOnBackdropClick) {
      this.props.onClose();
    }
  },

  handleModalClick: function(event) {
    event.stopPropagation();
  },

  render: function() {
    return React.createElement(ReactGateway, null,
      React.createElement('div', {
        className: this.backdropClassName,
        styles: this.backdropStyles,
        onClick: this.handleBackdropClick
      },
        React.createElement('div', {
          ref: 'modal',
          className: this.modalClassName,
          styles: this.modalStyles,
          onClick: this.handleModalClick,
          tabIndex: '-1'
        },
          this.props.children
        )
      )
    );
  }
});
<commit_msg>this.x => this.props.x<commit_after>'use strict';

var React = require('react');
var ReactDOM = require('react-dom');
var ReactGateway = require('react-gateway');
var focusScope = require('a11y-focus-scope');
var focusStore = require('a11y-focus-store');

var applicationElement;

function setApplicationElement() {
  applicationElement = element;
}

function setFocusOn(element) {
  focusStore.storeFocus();
  if (applicationElement) applicationElement.setAttribute('aria-hidden', 'true');
  focusScope.scopeFocus(this.refs.modal);
}

function resetFocus() {
  focusScope.unscopeFocus();
  if (applicationElement) applicationElement.removeAttribute('aria-hidden');
  focusStore.restoreFocus();
}

module.exports = React.createClass({
  displayName: 'ReactModal2',

  statics: {
    setApplicationElement: setApplicationElement
  },

  propTypes: {
    onClose: React.PropTypes.func.isRequired,

    closeOnEsc: React.PropTypes.bool,
    closeOnBackdropClick: React.PropTypes.bool,

    backdropClassName: React.PropTypes.string,
    backdropStyles: React.PropTypes.object,

    modalClassName: React.PropTypes.string,
    modalStyles: React.PropTypes.object
  },

  getDefaultProps: function() {
    return {
      closeOnEsc: true,
      closeOnBackdropClick: true
    };
  },

  componentDidMount: function() {
    setFocusOn(this.refs.modal);
    document.addEventListener('keydown', this.handleDocumentKeydown);
  },

  componentWillUnmount: function() {
    resetFocus();
    document.removeEventListener('keydown', this.handleDocumentKeydown);
  },

  handleDocumentKeydown: function(event) {
    if (this.props.closeOnEsc && event.keyCode === 27) {
      this.props.onClose();
    }
  },

  handleBackdropClick: function() {
    if (this.props.closeOnBackdropClick) {
      this.props.onClose();
    }
  },

  handleModalClick: function(event) {
    event.stopPropagation();
  },

  render: function() {
    return React.createElement(ReactGateway, null,
      React.createElement('div', {
        className: this.props.backdropClassName,
        styles: this.props.backdropStyles,
        onClick: this.handleBackdropClick
      },
        React.createElement('div', {
          ref: 'modal',
          className: this.props.modalClassName,
          styles: this.props.modalStyles,
          onClick: this.handleModalClick,
          tabIndex: '-1'
        },
          this.props.children
        )
      )
    );
  }
});
<|endoftext|>


<commit_before>
'use strict';

var config = require('./config');
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var http = require('http');
var server = app.listen(config.HOST_PORT);
var io = require('socket.io').listen(server);
var router = express.Router();
var _ = require('underscore');
var getmac = require('getmac');
var roomTracker = require('./roomTracker');

var subscribers = [];

app.use(express.static(__dirname + '/client'));
// app.use('/api/someaction/v1', require('./somefile.js'));

var favicon = require('serve-favicon');
app.use(favicon(__dirname + '/client/img/favicon.ico'));

console.log('listening on port: ',config.HOST_PORT);

/* Socket.io */
var numUsers = 0;
var nsp = io.of('/telem');
nsp.on('connection', function(socket){
    var addedUser = false;
    socket.on('add user', function (username){
        if(addedUser) return;

        ++numUsers;
        addedUser = true;
    });

    socket.on('disconnect', function(){
        if(addedUser)
            --numUsers;
    });
});

/* Poll for data */
var pollXandem = function(){
    var post_req = http.request({
        method: 'POST',
        host: config.XANDEM_HOST,
           path: '/v1/data',
           headers: {Authorization: config.XANDEM_API_KEY}
    }, function(response) {
        var body = '';
        response.on('data', function(d) {
            body += d;
        });
        response.on('end', function() {
            var parsed = JSON.parse(body);

            parsed.rooms = roomTracker.getOccupiedRooms(parsed.motion_coordinates);
            sendDataToSubscribers(parsed);

            if(numUsers>0){
                nsp.emit('newData', JSON.stringify(parsed));
            }

            setTimeout(pollXandem, config.XANDEM_POLLING_FREQUENCY);
        });

    });

    post_req.write(JSON.stringify({
        data_fields: ["motion_score", "motion_coordinates"]
    }));
    post_req.end();

    post_req.on('error', function(err) {
        console.error('Error with the request:', err.message);
    });

};
pollXandem();

var lastRoomsWithMotion = [];
function sendDataToSubscribers(data){

    //send only if different rooms are active
    if(_.isEqual(data.rooms, lastRoomsWithMotion)){
        return
    }else{
        lastRoomsWithMotion = data.rooms;
    }

    for(var i in subscribers){
        var post_req = http.request({
            method: 'POST',
            host: subscribers[i].ip,
            port: subscribers[i].port,
               path: subscribers[i].path,
               headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }
        }, function(response){});
        post_req.write(JSON.stringify(data));
        post_req.end();

        post_req.on('error', function(err) {
            console.error('Error with forwarding the data', err.message);
        });
    }
}

/* SSDP */
// var Server = require('node-ssdp').Server;
// var ssdpServer = new Server({
//     // unicastHost: require('ip').address(),
//     location: '/desc.xml',
//     // ssdpPort: '3000',
//     unicastBindPort: '3000',
//     // deviceAddress: '3000'
//     // ssdpIp: require('ip').address()
//     // location: require('ip').address() + ':3000/desc.xml',
//     // ssdpPort: 3000
// });

// // ssdpServer.addUSN('upnp:rootdevice')
// ssdpServer.addUSN('urn:schemas-upnp-org:device:XandemBridgeThang:1')
// // ssdpServer.addUSN('urn:schemas-upnp-org:service:ContentDirectory:1')
// // ssdpServer.addUSN('urn:schemas-upnp-org:service:ConnectionManager:1')
// ssdpServer.start(require('ip').address());

/* API */
app.use(bodyParser.json())

router.use(function(req, res, next) {
    // do logging
    // console.log('Something is happening.');
    next(); // make sure we go to the next routes and don't stop here
});
router.post('/register', function(req, res){
    var newSubscriber  = req.body.content;

    if(_.isEqual(newSubscriber,{})){
        var errorMessage = "Subscription request malformatted. The body was null";
        res.json({ message: errorMessage, subscriptionStatus: false });
        console.error(errorMessage);
        return;
    }

    if(!newSubscriber.ip){
        res.json({ message: 'Please specify an IP Address', subscriptionStatus: false });
    }else if(!newSubscriber.port){
        res.json({ message: 'Please specify a Port', subscriptionStatus: false });
    }else if(!newSubscriber.path){
        newSubscriber.path = '/';
    }
    if(_.findIndex(subscribers,newSubscriber)>=0){
        res.json({ message: 'You\'ve already subscribed. Don\'t ask me again!', subscriptionStatus: true });
    }else{
        subscribers.push(newSubscriber);
        console.log('Subscribers: ',subscribers);
        res.json({ message: 'Hello There! welcome to the api!', subscriptionStatus: true });
    }

});

var macAddress;
getmac.getMac(function(err,_macAddress_){ macAddress = _macAddress_; });
router.get('/info', function(req, res){
    res.json({macAddress: macAddress});
});

router.get('/rooms', function(req, res){
    res.json({rooms: config.ROOMS});
});
app.use('/api', router);
<commit_msg>fixed merge conflict<commit_after>
'use strict';

var config = require('./config');
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var http = require('http');
var server = app.listen(config.HOST_PORT);
var io = require('socket.io').listen(server);
var router = express.Router();
var _ = require('underscore');
var getmac = require('getmac');
var roomTracker = require('./roomTracker');

var subscribers = [];

app.use(express.static(__dirname + '/client'));
// app.use('/api/someaction/v1', require('./somefile.js'));

var favicon = require('serve-favicon');
app.use(favicon(__dirname + '/client/img/favicon.ico'));

console.log('listening on port: ',config.HOST_PORT);

/* Socket.io */
var numUsers = 0;
var nsp = io.of('/telem');
nsp.on('connection', function(socket){
    var addedUser = false;
    socket.on('add user', function (username){
        if(addedUser) return;

        ++numUsers;
        addedUser = true;
    });

    socket.on('disconnect', function(){
        if(addedUser)
            --numUsers;
    });
});

/* Poll for data */
var pollXandem = function(){
    var post_req = http.request({
        method: 'POST',
        host: config.XANDEM_HOST,
           path: '/v1/data',
           headers: {Authorization: config.XANDEM_API_KEY}
    }, function(response) {
        var body = '';
        response.on('data', function(d) {
            body += d;
        });
        response.on('end', function() {
            var parsed = JSON.parse(body);
<<<<<<< HEAD

            parsed.rooms = roomTracker.getOccupiedRooms(parsed.motion_coordinates);
=======
            parsed.rooms = roomTracker.getOccupiedRooms(parsed.motion_coordinates);
>>>>>>> d6946ac989e084e0f7e6056af80be50ce0ed5e21
            sendDataToSubscribers(parsed);

            if(numUsers>0){
                nsp.emit('newData', JSON.stringify(parsed));
            }

            setTimeout(pollXandem, config.XANDEM_POLLING_FREQUENCY);
        });

    });

    post_req.write(JSON.stringify({
        data_fields: ["motion_score", "motion_coordinates"]
    }));
    post_req.end();

    post_req.on('error', function(err) {
        console.error('Error with the request:', err.message);
    });

};
pollXandem();

var lastRoomsWithMotion = [];
function sendDataToSubscribers(data){

    //send only if different rooms are active
    if(_.isEqual(data.rooms, lastRoomsWithMotion)){
        return
    }else{
        lastRoomsWithMotion = data.rooms;
    }

    for(var i in subscribers){
        var post_req = http.request({
            method: 'POST',
            host: subscribers[i].ip,
            port: subscribers[i].port,
               path: subscribers[i].path,
               headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }
        }, function(response){});
        post_req.write(JSON.stringify(data));
        post_req.end();

        post_req.on('error', function(err) {
            console.error('Error with forwarding the data', err.message);
        });
    }
}

var interval = setInterval(function(){
    if(lastRoomsWithMotion.length > 0){
            var data = {};
            data.rooms = lastRoomsWithMotion;
        for(var i in subscribers){
            var post_req = http.request({
                method: 'POST',
                    host: subscribers[i].ip,
                    port: subscribers[i].port,
                       path: subscribers[i].path,
                       headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }
                }, function(response){});
            post_req.write(JSON.stringify(data));
            post_req.end();

            post_req.on('error', function(err) {
                console.error('Error with forwarding the data', err.message);
            });
        }
    }

},1000);

/* SSDP */
// var Server = require('node-ssdp').Server;
// var ssdpServer = new Server({
//     // unicastHost: require('ip').address(),
//     location: '/desc.xml',
//     // ssdpPort: '3000',
//     unicastBindPort: '3000',
//     // deviceAddress: '3000'
//     // ssdpIp: require('ip').address()
//     // location: require('ip').address() + ':3000/desc.xml',
//     // ssdpPort: 3000
// });

// // ssdpServer.addUSN('upnp:rootdevice')
// ssdpServer.addUSN('urn:schemas-upnp-org:device:XandemBridgeThang:1')
// // ssdpServer.addUSN('urn:schemas-upnp-org:service:ContentDirectory:1')
// // ssdpServer.addUSN('urn:schemas-upnp-org:service:ConnectionManager:1')
// ssdpServer.start(require('ip').address());

/* API */
app.use(bodyParser.json())

router.use(function(req, res, next) {
    // do logging
    // console.log('Something is happening.');
    next(); // make sure we go to the next routes and don't stop here
});
router.post('/register', function(req, res){
    var newSubscriber  = req.body.content;

    if(_.isEqual(newSubscriber,{})){
        var errorMessage = "Subscription request malformatted. The body was null";
        res.json({ message: errorMessage, subscriptionStatus: false });
        console.error(errorMessage);
        return;
    }

    if(!newSubscriber.ip){
        res.json({ message: 'Please specify an IP Address', subscriptionStatus: false });
    }else if(!newSubscriber.port){
        res.json({ message: 'Please specify a Port', subscriptionStatus: false });
    }else if(!newSubscriber.path){
        newSubscriber.path = '/';
    }
    if(_.findIndex(subscribers,newSubscriber)>=0){
        res.json({ message: 'You\'ve already subscribed. Don\'t ask me again!', subscriptionStatus: true });
    }else{
        subscribers.push(newSubscriber);
        console.log('Subscribers: ',subscribers);
        res.json({ message: 'Hello There! welcome to the api!', subscriptionStatus: true });
    }

});

var macAddress;
getmac.getMac(function(err,_macAddress_){ macAddress = _macAddress_; });
router.get('/info', function(req, res){
    res.json({macAddress: macAddress});
});

router.get('/rooms', function(req, res){
    res.json({rooms: config.ROOMS});
});
app.use('/api', router);
<|endoftext|>


<commit_before>/*
 * @copyright Copyright (c) Sematext Group, Inc. - All Rights Reserved
 *
 * @licence SPM for NodeJS is free-to-use, proprietary software.
 * THIS IS PROPRIETARY SOURCE CODE OF Sematext Group, Inc. (Sematext)
 * This source code may not be copied, reverse engineered, or altered for any purpose.
 * This source code is to be used exclusively by users and customers of Sematext.
 * Please see the full license (found in LICENSE in this distribution) for details on its license and the licenses of its dependencies.
 */
'user strict'
var MAX_LOGS = process.env.LOGSENE_BULK_SIZE || 1000
var MAX_STORED_REQUESTS = process.env.LOGSENE_MAX_STORED_REQUESTS || 10000
var MAX_CLIENT_SOCKETS = Number(process.env.MAX_CLIENT_SOCKETS) || 10
var request = require('request')
var os = require('os')
var events = require('events')
var ipAddress = require('ip').address()
var util = require('util')
var path = require('path')
var stringifySafe = require('json-stringify-safe')
var streamBuffers = require('stream-buffers')
var initialBufferSize = 1024 * 256
var incrementBuffer = 1024 * 256
/**
 * token - the LOGSENE Token
 * type - type of log (string)
 * url - optional alternative URL for Logsene receiver (e.g. for on premises version)
 */
function Logsene (token, type, url, storageDirectory) {
  if (!token) {
    throw new Error('Logsene token not specified')
  }
  this.setUrl(url || process.env.LOGSENE_URL || 'https://logsene-receiver.sematext.com/_bulk')
  this.token = token
  this.type = type || 'logs'
  this.hostname = os.hostname()
  this.bulkReq = new streamBuffers.WritableStreamBuffer({
    initialSize: initialBufferSize,
    incrementAmount: incrementBuffer
  })
  this.offset
  this.logCount = 0
  this.sourceName = null
  if (process.mainModule && process.mainModule.filename) {
    this.sourceName = path.basename(process.mainModule.filename)
  }
  events.EventEmitter.call(this)
  var self = this
  var tid = setInterval(function () {
    if (self.logCount > 0) {
      self.send()
    }
  }, process.env.LOGSENE_LOG_INTERVAL || 10000)
  if (tid.unref) {
    tid.unref()
  }
  process.on('exit', function () {
    self.send()
  })
  if (process.env.LOGSENE_TMP_DIR || storageDirectory) {
    this.diskBuffer(true, process.env.LOGSENE_TMP_DIR || storageDirectory)
  }
}
util.inherits(Logsene, events.EventEmitter)

Logsene.prototype.setUrl = function (url) {
  this.url = url
  var Agent = null
  if (/^https/.test(url)) {
    Agent = require('https').Agent
  } else {
    Agent = require('http').Agent
  }
  this.httpAgent = new Agent({maxSockets: MAX_CLIENT_SOCKETS})
}
var DiskBuffer = require('./DiskBuffer.js')

Logsene.prototype.diskBuffer = function (enabled, dir) {
  if (enabled) {
    var tmpDir = path.join((dir || require('os').tmpdir()), this.token)
    this.db = DiskBuffer.createDiskBuffer({
      tmpDir: tmpDir,
      maxStoredRequests: MAX_STORED_REQUESTS,
      interval: process.env.LOGSENE_DISK_BUFFER_INTERVAL || 120000
    })
    this.db.syncFileListFromDir()
    this.db.on('retransmit-req', function (event) {
      this.shipFile(event.fileName, event.buffer, function (err, res) {
        this.db.rmFile(event.fileName)
        this.db.retransmitNext()
      }.bind(this))
    }.bind(this))
  }
  this.persistence = enabled
}

/**
 * Add log message to send buffer
 * @param level - log level e.g. 'info', 'warning', 'error'
 * @param message - text message
 * @param fields - Object with custom fields or overwrite of any other field e.g. e.g. "{@timestamp: new Date.toISOString()}"
 * @param callback (err, msg object)
 */
Logsene.prototype.log = function (level, message, fields, callback) {
  var type = fields ? fields._type : this.type
  if (fields && fields._type) {
    delete fields._type
  }
  var msg = {'@timestamp': new Date(), severity: level, host: this.hostname, ip: ipAddress, message: message, '@source': this.sourceName}
  for (var x in fields) {
    // rename fields for Elasticsearch 2.x
    msg[x.replace(/\./g, '_').replace(/^_+/, '')] = fields[x]
  }
  if (typeof msg['@timestamp'] === 'number') {
    msg['@timestamp'] = new Date(msg['@timestamp'])
  }
  this.bulkReq.write(JSON.stringify({'index': {'_index': this.token, '_type': type || this.type}}) + '\n')
  this.bulkReq.write(stringifySafe(msg) + '\n')
  this.logCount++
  if (this.logCount === MAX_LOGS) {
    this.bulkReq.end()
    this.send()
  }
  if (callback) {
    callback(null, msg)
  }
}

/**
 * Sending log entry  to LOGSENE - this function is triggered every 100 log message or 30 seconds.
 * @callback {function} optional callback function
 */
Logsene.prototype.send = function (callback) {
  var self = this
  var count = this.logCount
  this.logCount = 0
  var options = {
    url: this.url,
    logCount: count,
    headers: {
      'User-Agent': 'logsene-js',
      'Content-Type': 'application/json',
      'Connection': 'Close'
    },
    body: this.bulkReq.getContents(),
    agent: self.httpAgent,
    method: 'POST'
  }
  this.bulkReq = null
  this.bulkReq = new streamBuffers.WritableStreamBuffer({
    initialSize: initialBufferSize,
    incrementAmount: incrementBuffer
  })
  if (options.body === false) {
    return
  }
  var req = request.post(options, function (err, res) {
    if (err) {
      self.emit('error', {source: 'logsene', err: err})
      if (self.persistence) {
        options.agent = false
        self.db.store({options: options}, function () {
          delete options.body
        })
        return
      }
      req.destroy()
    } else {
      self.emit('log', {source: 'logsene', count: count})
      if (callback) {
        callback(null, res)
      }
      delete options.body
      req.destroy()
    }
  })
}

Logsene.prototype.shipFile = function (name, data, cb) {
  var self = this
  var options = JSON.parse(data)
  options.url = self.url
  options.agent = self.httpAgent
  var req = request.post(options, function (err, res) {
    if (cb) {
      cb(err, res)
    }
    if (err) {
      self.emit('error', {source: 'logsene', err: err})
      if (self.persistence) {
        options.agent = false
        self.db.store({options: options}, function () {
          delete options.body
        })
      }
    } else {
      self.emit('file shipped', {file: name, count: options.count})
      self.emit('rt', {count: options.count, source: 'logsene', file: name, url: String(options.url), request: null, response: null})
    }
    req.destroy()
  })
}

module.exports = Logsene
<commit_msg>rename '@source' field to 'logSource'<commit_after>/*
 * @copyright Copyright (c) Sematext Group, Inc. - All Rights Reserved
 *
 * @licence SPM for NodeJS is free-to-use, proprietary software.
 * THIS IS PROPRIETARY SOURCE CODE OF Sematext Group, Inc. (Sematext)
 * This source code may not be copied, reverse engineered, or altered for any purpose.
 * This source code is to be used exclusively by users and customers of Sematext.
 * Please see the full license (found in LICENSE in this distribution) for details on its license and the licenses of its dependencies.
 */
'user strict'
var MAX_LOGS = process.env.LOGSENE_BULK_SIZE || 1000
var MAX_STORED_REQUESTS = process.env.LOGSENE_MAX_STORED_REQUESTS || 10000
var MAX_CLIENT_SOCKETS = Number(process.env.MAX_CLIENT_SOCKETS) || 10
var request = require('request')
var os = require('os')
var events = require('events')
var ipAddress = require('ip').address()
var util = require('util')
var path = require('path')
var stringifySafe = require('json-stringify-safe')
var streamBuffers = require('stream-buffers')
var initialBufferSize = 1024 * 256
var incrementBuffer = 1024 * 256
/**
 * token - the LOGSENE Token
 * type - type of log (string)
 * url - optional alternative URL for Logsene receiver (e.g. for on premises version)
 */
function Logsene (token, type, url, storageDirectory) {
  if (!token) {
    throw new Error('Logsene token not specified')
  }
  this.setUrl(url || process.env.LOGSENE_URL || 'https://logsene-receiver.sematext.com/_bulk')
  this.token = token
  this.type = type || 'logs'
  this.hostname = os.hostname()
  this.bulkReq = new streamBuffers.WritableStreamBuffer({
    initialSize: initialBufferSize,
    incrementAmount: incrementBuffer
  })
  this.offset
  this.logCount = 0
  this.sourceName = null
  if (process.mainModule && process.mainModule.filename) {
    this.sourceName = path.basename(process.mainModule.filename)
  }
  events.EventEmitter.call(this)
  var self = this
  var tid = setInterval(function () {
    if (self.logCount > 0) {
      self.send()
    }
  }, process.env.LOGSENE_LOG_INTERVAL || 10000)
  if (tid.unref) {
    tid.unref()
  }
  process.on('exit', function () {
    self.send()
  })
  if (process.env.LOGSENE_TMP_DIR || storageDirectory) {
    this.diskBuffer(true, process.env.LOGSENE_TMP_DIR || storageDirectory)
  }
}
util.inherits(Logsene, events.EventEmitter)

Logsene.prototype.setUrl = function (url) {
  this.url = url
  var Agent = null
  if (/^https/.test(url)) {
    Agent = require('https').Agent
  } else {
    Agent = require('http').Agent
  }
  this.httpAgent = new Agent({maxSockets: MAX_CLIENT_SOCKETS})
}
var DiskBuffer = require('./DiskBuffer.js')

Logsene.prototype.diskBuffer = function (enabled, dir) {
  if (enabled) {
    var tmpDir = path.join((dir || require('os').tmpdir()), this.token)
    this.db = DiskBuffer.createDiskBuffer({
      tmpDir: tmpDir,
      maxStoredRequests: MAX_STORED_REQUESTS,
      interval: process.env.LOGSENE_DISK_BUFFER_INTERVAL || 120000
    })
    this.db.syncFileListFromDir()
    this.db.on('retransmit-req', function (event) {
      this.shipFile(event.fileName, event.buffer, function (err, res) {
        this.db.rmFile(event.fileName)
        this.db.retransmitNext()
      }.bind(this))
    }.bind(this))
  }
  this.persistence = enabled
}

/**
 * Add log message to send buffer
 * @param level - log level e.g. 'info', 'warning', 'error'
 * @param message - text message
 * @param fields - Object with custom fields or overwrite of any other field e.g. e.g. "{@timestamp: new Date.toISOString()}"
 * @param callback (err, msg object)
 */
Logsene.prototype.log = function (level, message, fields, callback) {
  var type = fields ? fields._type : this.type
  if (fields && fields._type) {
    delete fields._type
  }
  var msg = {'@timestamp': new Date(), severity: level, host: this.hostname, ip: ipAddress, message: message, logSource: this.sourceName}
  for (var x in fields) {
    // rename fields for Elasticsearch 2.x
    msg[x.replace(/\./g, '_').replace(/^_+/, '')] = fields[x]
  }
  if (typeof msg['@timestamp'] === 'number') {
    msg['@timestamp'] = new Date(msg['@timestamp'])
  }
  this.bulkReq.write(JSON.stringify({'index': {'_index': this.token, '_type': type || this.type}}) + '\n')
  this.bulkReq.write(stringifySafe(msg) + '\n')
  this.logCount++
  if (this.logCount === MAX_LOGS) {
    this.bulkReq.end()
    this.send()
  }
  if (callback) {
    callback(null, msg)
  }
}

/**
 * Sending log entry  to LOGSENE - this function is triggered every 100 log message or 30 seconds.
 * @callback {function} optional callback function
 */
Logsene.prototype.send = function (callback) {
  var self = this
  var count = this.logCount
  this.logCount = 0
  var options = {
    url: this.url,
    logCount: count,
    headers: {
      'User-Agent': 'logsene-js',
      'Content-Type': 'application/json',
      'Connection': 'Close'
    },
    body: this.bulkReq.getContents(),
    agent: self.httpAgent,
    method: 'POST'
  }
  this.bulkReq = null
  this.bulkReq = new streamBuffers.WritableStreamBuffer({
    initialSize: initialBufferSize,
    incrementAmount: incrementBuffer
  })
  if (options.body === false) {
    return
  }
  var req = request.post(options, function (err, res) {
    if (err) {
      self.emit('error', {source: 'logsene', err: err})
      if (self.persistence) {
        options.agent = false
        self.db.store({options: options}, function () {
          delete options.body
        })
        return
      }
      req.destroy()
    } else {
      self.emit('log', {source: 'logsene', count: count})
      if (callback) {
        callback(null, res)
      }
      delete options.body
      req.destroy()
    }
  })
}

Logsene.prototype.shipFile = function (name, data, cb) {
  var self = this
  var options = JSON.parse(data)
  options.url = self.url
  options.agent = self.httpAgent
  var req = request.post(options, function (err, res) {
    if (cb) {
      cb(err, res)
    }
    if (err) {
      self.emit('error', {source: 'logsene', err: err})
      if (self.persistence) {
        options.agent = false
        self.db.store({options: options}, function () {
          delete options.body
        })
      }
    } else {
      self.emit('file shipped', {file: name, count: options.count})
      self.emit('rt', {count: options.count, source: 'logsene', file: name, url: String(options.url), request: null, response: null})
    }
    req.destroy()
  })
}

module.exports = Logsene
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');

const stringify = require('csv-stringify');
const request = require('request');

const providers = require('./providers');

module.exports = function(config) {
  let results = [["times"]];

  for (let i = 0; i < config.test.timings.length; i++) {
    results[0][i + 1] = i + 1;
    results[i + 1] = [config.test.timings[i]];
  }

  let remainingIterations = config.test.iterations;

  providers.prepareConcurrency(config, () => {
    let executeRound = (currentRound) => {
      providers.deploy(config, (uris) => {
        if (uris.length > remainingIterations) {
          uris = uris.slice(0, remainingIterations);
        }

        let remainingInRound = uris.length;

        for (let i = 0; i < uris.length; i++) {
          let remainingInIteration = config.test.timings.length;

          let delay = 3 * config.test.timings.length * config.test.concurrency;
          let start = Date.now() + delay;

          for (let n = 0; n < config.test.timings.length; n++) {
            setTimeout(
              (currentIteration) => request.post({
                url: uris[i],
                body: JSON.stringify({ duration: config.function.duration }),
                time: true
              }, (err, res, body) => {
                if (err) throw err;
                if (res.statusCode != 200) {
                  throw new Error('Unexpected response. Status code: ' + res.statusCode + '. Body: ' + body);
                }

                let overhead = res.elapsedTime - JSON.parse(body).duration;
                console.log(overhead + 'ms');
                results[n + 1][currentIteration + 1] = overhead;

                if (--remainingInIteration == 0) {
                  console.log('Finished iteration ' + currentIteration);

                  if (--remainingIterations == 0) {
                    stringify(results, (err, output) => {
                      if (err) throw err;
                      fs.writeFile(config.resultsFile, output);
                      providers.cleanupDeployment(config);
                    });
                  } else if (--remainingInRound == 0) {
                    executeRound(currentRound + 1);
                  }
                }
              }),
              start + config.test.timings[n] - Date.now(),
              currentRound * config.test.concurrency + i
            );
          }
        }
      });
    };
    executeRound(0);
  });
}
<commit_msg>fix headers in results file<commit_after>'use strict';

const fs = require('fs');

const stringify = require('csv-stringify');
const request = require('request');

const providers = require('./providers');

module.exports = function(config) {
  let results = [["times"]];

  for (let i = 0; i < config.test.timings.length; i++) {
    results[i + 1] = [config.test.timings[i]];
  }

  for (let i = 0; i < config.test.iterations; i++) {
    results[0][i + 1] = i;
  }

  let remainingIterations = config.test.iterations;

  providers.prepareConcurrency(config, () => {
    let executeRound = (currentRound) => {
      providers.deploy(config, (uris) => {
        if (uris.length > remainingIterations) {
          uris = uris.slice(0, remainingIterations);
        }

        let remainingInRound = uris.length;

        for (let i = 0; i < uris.length; i++) {
          let remainingInIteration = config.test.timings.length;

          let delay = 3 * config.test.timings.length * config.test.concurrency;
          let start = Date.now() + delay;

          for (let n = 0; n < config.test.timings.length; n++) {
            setTimeout(
              (currentIteration) => request.post({
                url: uris[i],
                body: JSON.stringify({ duration: config.function.duration }),
                time: true
              }, (err, res, body) => {
                if (err) throw err;
                if (res.statusCode != 200) {
                  throw new Error('Unexpected response. Status code: ' + res.statusCode + '. Body: ' + body);
                }

                let overhead = res.elapsedTime - JSON.parse(body).duration;
                console.log(overhead + 'ms');
                results[n + 1][currentIteration + 1] = overhead;

                if (--remainingInIteration == 0) {
                  console.log('Finished iteration ' + currentIteration);

                  if (--remainingIterations == 0) {
                    stringify(results, (err, output) => {
                      if (err) throw err;
                      fs.writeFile(config.resultsFile, output);
                      providers.cleanupDeployment(config);
                    });
                  } else if (--remainingInRound == 0) {
                    executeRound(currentRound + 1);
                  }
                }
              }),
              start + config.test.timings[n] - Date.now(),
              currentRound * config.test.concurrency + i
            );
          }
        }
      });
    };
    executeRound(0);
  });
}
<|endoftext|>