<commit_before>var axon = require('axon')
  , os = require('os')
  , util = require('util')
  , ip = require('ip')
  , request = require('request')
  , registrations = require('thalassa-registrations')
  , EventEmitter = require('events').EventEmitter
  , deepEqual = require('deep-equal')
  ;

/**
 * `Client` constructor
 * @constructor
 *
 * @param {number} [opts.port=5001] - Port number of axon socket port
 * @param {number} [opts.apiport=9000] - Port number of Thalassa HTTP API
 * @param {String} [opts.host=127.0.0.1] - Thalassa host
 * @param {number} [opts.updateFreq=20000] - How often to check into registrations to Thalassa server
 * @param {number} [opts.updateTimeout=2500] - How long to wait for a registrion request to respond
 * @param {String} [opts.mode=http]
 */

var Client = module.exports = function (opts) {
  if (typeof opts !== 'object') opts = {};
  this.log = (typeof opts.log === 'function') ? opts.log : function (){};

  EventEmitter.call(this);

  this.APIPORT        = opts.apiport || 9000;
  this.PORT           = opts.port || 5001;
  this.HOST           = opts.host || '127.0.0.1';
  this.UPDATE_FREQ    = opts.updateFreq || 20000;
  this.TIMEOUT        = opts.updateTimeout || 2500;
  this.SEC_TO_EXPIRE  = opts.secsToExpire || 60;
  this.MAX_PENDING    = opts.maxPending || 5;
  this.MODE           = opts.mode || 'http';
  this.MY_IP          = ip.address();

  this.isOn = false;
  this.intents = [];
  this.registrations = {};
  this.attempts = {};

  this.socket = null;
};

util.inherits(Client, EventEmitter);

/**
 * Create a new registration
 *
 * @param {String} name - Name of the service (preferably require('./package.json').name)
 * @param {String} version - Version of the service (preferably require('./package.json').version)
 * @param {String} port - Port this registration is bound to that clients can call
 * @param {Object} meta - Any additional meta data (key, value) to include
 */

Client.prototype.register = function(name, version, port, meta) {
  var self = this;
  var reg = {
    name: name,
    version: version,
    host: self.MY_IP,
    port: port,
    meta: meta || {}
  };

  if (!reg.meta.hostname) {
    reg.meta.hostname = reg.host;
  }

  if (!reg.meta.secondsToExpire) {
    reg.meta.secondsToExpire = this.SEC_TO_EXPIRE;
  }

  reg.meta.hostname = os.hostname();
  reg.meta.pid = process.pid;
  reg.meta.registered = Date.now();

  var intent = registrations.create(reg);
  self.intents.push(intent);

  if (self.isOn) {
    self._sendUpdate(intent);
  }
};

/**
 * Unregister a previously registered registration
 *
 * @param {String} name - Name of the service (preferably require('./package.json').name)
 * @param {String} version - Version of the service (preferably require('./package.json').version)
 * @param {String} port - Port this registration is bound to that clients can call
 */

Client.prototype.unregister = function(name, version, port) {
  var self = this;
  var host = self.MY_IP;
  var reg = registrations.create({
    name: name,
    version: version,
    host: host,
    port: port,
  });

  //
  // filter out the unwanted registration intent
  //
  self.intents = self.intents.filter(function (intent) {
    return reg.id !== intent.id;
  });

  //
  // explicitely delete the registration so the offline notifications happen immediately
  //
  self.del(name, version, host, port);
};

/**
 * Explicitly delete a registration
 *
 * @param {String} name - Name of the service
 * @param {String} version - Version of the service
 * @param {String} port - Port of the registration
 * @param {String} host - Host (ip) of the registration
 */
Client.prototype.del = function (name, version, host, port, cb) {
  var self = this;
  var uri = util.format('http://%s:%s/registrations/%s/%s/%s/%s', self.HOST, self.APIPORT, name, version, host, port);
  request({
    uri: uri,
    json: true,
    method: 'DELETE'
  },
  function (error, response, body) {
    if (error) self.log('error', 'Thalassa:Client.del', error);
    if (response && response.statusCode !== 200 && response.statusCode !== 404) {
      self.log('error', 'Thalassa:Client.del unexpected response ' + response.statusCode);
      error = new Error("del unexpected response " + response.statusCode);
    }
    if (typeof cb === 'function') cb(error);
  });
};

/**
 * Start polling, periodically checking the registrations into the Thalassa server
 */

Client.prototype.start = function() {
  var self = this;
  if (!self.isOn) {
    self.isOn = true;
    self._startUpdateInterval();
  }
};

/**
 * Stop polling registrations
 */

Client.prototype.stop = function() {
  var self = this;
  self.isOn = false;
  clearInterval(self._updateInterval);
  Object.keys(self.registrations).forEach(function (key) {
    var reg = self.registrations[key];
    delete self.registrations[key];
    self.unregister(reg.name,reg.version,reg.port);
  });
};

/**
 * Close: stop polling, disconnect socket
 */

Client.prototype.close = function() {
  var self = this;
  this.stop();
  if (this.socket) this.socket.close();
};

/**
 * Find all `Registration`s over HTTP for `name` and `version` if provided.
 *
 * @param {String} [name]
 * @param {String} [version]
 * @param {getRegistrationsCallback} cb - Callback cb(error, registrations)
 *
 * @callback getRegistrationsCallback
 * @param {Error} error
 * @param {Registrations[]} registrations
 */

Client.prototype.getRegistrations = function(name, version, cb) {
  var self = this;

  var path;
  if (typeof name === 'function') {
    cb = name;
    path = '/registrations';
  }
  else if (typeof version === 'function') {
    cb = version;
    path = util.format('/registrations/%s', name);
  }
  else if (typeof cb === 'function') {
    path = util.format('/registrations/%s/%s', name, version);
  }

  var uri = util.format('http://%s:%s%s', self.HOST, self.APIPORT, path);

  self.log('debug', 'Thalassa:Client.getRegistrations uri: ' + uri);

  request({
    uri: uri,
    json: true
  },
  function (error, response, body) {
    if (error) self.log('error', 'Thalassa:Client.getRegistrations', error);
    if (response && response.statusCode !== 200 && response.statusCode !== 404) {
      self.log('error', 'Thalassa:Client.getRegistrations unexpected response ' + response.statusCode);
      error = new Error("getRegistrations unexpected response " + response.statusCode);
    }
    if (error) return cb(error);

    var regs = (response.statusCode !== 200) ? [] : body;
    cb(null, regs);
  });
};

//
// Axon socket functions
//

/**
 * Subscribe to `offline` and `online` events over the axon socket. Connects if not
 * already connected.
 *
 * @param {String} [name]
 * @param {String} [version]
 */

 Client.prototype.subscribe = function(name, version) {
  if (this.socket === null) this.socketConnect();
  var prefix = this._keySearch(name, version);
  this.log('debug', 'Thalassa:Client.subscribe ' + prefix);
  this.socket.subscribe(prefix);
};

/**
 * Unsubscribe to `offline` and `online` events over the axon socket. Connects if not
 * already connected.
 *
 * @param {String} [name]
 * @param {String} [version]
 */

Client.prototype.unsubscribe = function(name, version) {
  if (this.socket === null) this.socketConnect();
  var prefix = this._keySearch(name, version);
  this.log('info', 'Thalassa:Client.unsubscribe ' + prefix);
  this.socket.unsubscribe(prefix);
};

/**
 * Connect to the Thalassa Server axon socket if not already connected, typically called
 * the first time `subscribe` is called
 */
Client.prototype.socketConnect = function() {
  var self = this;
  if (this.socket === null) {
    this.socket = axon.socket('sub');
    this.socket.set('identity', this.MY_IP + ':' + this.PORT);
    self.log('info', 'Thalassa:Client.socketConnect: connecting to socket ' + this.HOST + ':' + this.PORT);
    this.socket.connect(this.PORT, this.HOST);

    this.socket.on('message', function (regId, state, reg) {
      regId = regId.toString();
      state = state.toString();

      if (state === 'online') {
        var updatedReg = JSON.parse(reg);
        if(regId in self.registrations){
          //if we already have the registration, and no metadata changed, do nothing
          if(deepEqual(updatedReg.meta, self.registrations[regId].meta))
            return;
        }
        self.registrations[regId] = updatedReg;
        self.emit('online', registrations.parse(reg.toString()));
      }
      else if (state === 'offline') {
        delete registrations[regId];
        self.emit('offline', regId);
      }
      else {
        self.log('info', 'Thalassa:Client.onMessage: ignoring message, unknown state ', arguments.map(function (b) { b.toString(); }));
      }
    });
  }
};

Client.prototype._keySearch = function(name, version) {
  var keySearch = (name) ? ('/' + name) : '';
  keySearch += (version) ? util.format('/%s/*', version) : '/*';
  return keySearch;
};

Client.prototype._startUpdateInterval = function() {
  var self = this;
  update();
  self._updateInterval = setInterval(update, self.UPDATE_FREQ);

  function update () {
    self.intents.forEach(self._sendUpdate.bind(self));
  }
};

Client.prototype._sendUpdate = function (intent) {
  if (this.MODE === 'http') {
    this._sendHTTPUpdate(intent);
  }
  else {
    this.log('error', 'Thalassa:Client._sendUpdate: unsupported mode ' + this.mode);
  }
};

Client.prototype._sendHTTPUpdate = function (intent) {
  // TODO batch multiple requests?
  var self = this;
  var uri = util.format('http://%s:%s/registrations/%s/%s/%s/%s', self.HOST, self.APIPORT, intent.name, intent.version, intent.host, intent.port);
  var startTime = Date.now();

  if(!(intent.id in self.attempts) || self.attempts[intent.id] == self.ATTEMPTS_BEFORE_RETRY){
    //If this is a case of hitting max attempts, log it
    if(intent.id in self.attempts){
      self.log('error', 'Reached max number of attempts (' + self.ATTEMPTS_BEFORE_RETRY + ') waiting for response, sending new request');
    }
    self.attempts[intent.id] = 0;
  }

  // If we have a request still pending, increment the number of attempts and return
  if (self.attempts[intent.id] > 0) {
    self.attempts[intent.id]++;
    self.log('error', 'Thalassa:Client._sendHTTPUpdate call still pending! (skipping): ' + intent.id);
    return;
  }

  self.attempts[intent.id]++;

  request({
    uri: uri,
    method: 'POST',
    json: intent.meta,
    timeout: self.TIMEOUT
  },
  function (error, response, body) {
    self.attempts[intent.id] = 0;

    // Check for an unsuccessful response, in case, for example we call the wrong
    // host and it returns a 404, etc.
    if (!error && response.statusCode !== 200) {
      error = new Error(util.format('unexpected response statusCode %s from %s',
                        response.statusCode, uri));
    }

    if (error) {
      self.log('error', 'Thalassa:Client._sendUpdate', error);
      self.emit('updateFailed', error);
    }
    else {
      self.emit('updateSuccessful');
      self.log('debug', util.format('Thalassa:Client._sendUpdate (%s) [%s] %s', response.statusCode, Date.now() - startTime, uri));
    }
  });
};
<commit_msg>Rename the client options for max attempts before retry<commit_after>var axon = require('axon')
  , os = require('os')
  , util = require('util')
  , ip = require('ip')
  , request = require('request')
  , registrations = require('thalassa-registrations')
  , EventEmitter = require('events').EventEmitter
  , deepEqual = require('deep-equal')
  ;

/**
 * `Client` constructor
 * @constructor
 *
 * @param {number} [opts.port=5001] - Port number of axon socket port
 * @param {number} [opts.apiport=9000] - Port number of Thalassa HTTP API
 * @param {String} [opts.host=127.0.0.1] - Thalassa host
 * @param {number} [opts.updateFreq=20000] - How often to check into registrations to Thalassa server
 * @param {number} [opts.updateTimeout=2500] - How long to wait for a registrion request to respond
 * @param {String} [opts.mode=http]
 */

var Client = module.exports = function (opts) {
  if (typeof opts !== 'object') opts = {};
  this.log = (typeof opts.log === 'function') ? opts.log : function (){};

  EventEmitter.call(this);

  this.APIPORT                  = opts.apiport || 9000;
  this.PORT                     = opts.port || 5001;
  this.HOST                     = opts.host || '127.0.0.1';
  this.UPDATE_FREQ              = opts.updateFreq || 20000;
  this.TIMEOUT                  = opts.updateTimeout || 2500;
  this.SEC_TO_EXPIRE            = opts.secsToExpire || 60;
  this.ATTEMPTS_BEFORE_RETRY    = opts.maxPending || 5;
  this.MODE                     = opts.mode || 'http';
  this.MY_IP                    = ip.address();

  this.isOn = false;
  this.intents = [];
  this.registrations = {};
  this.attempts = {};

  this.socket = null;
};

util.inherits(Client, EventEmitter);

/**
 * Create a new registration
 *
 * @param {String} name - Name of the service (preferably require('./package.json').name)
 * @param {String} version - Version of the service (preferably require('./package.json').version)
 * @param {String} port - Port this registration is bound to that clients can call
 * @param {Object} meta - Any additional meta data (key, value) to include
 */

Client.prototype.register = function(name, version, port, meta) {
  var self = this;
  var reg = {
    name: name,
    version: version,
    host: self.MY_IP,
    port: port,
    meta: meta || {}
  };

  if (!reg.meta.hostname) {
    reg.meta.hostname = reg.host;
  }

  if (!reg.meta.secondsToExpire) {
    reg.meta.secondsToExpire = this.SEC_TO_EXPIRE;
  }

  reg.meta.hostname = os.hostname();
  reg.meta.pid = process.pid;
  reg.meta.registered = Date.now();

  var intent = registrations.create(reg);
  self.intents.push(intent);

  if (self.isOn) {
    self._sendUpdate(intent);
  }
};

/**
 * Unregister a previously registered registration
 *
 * @param {String} name - Name of the service (preferably require('./package.json').name)
 * @param {String} version - Version of the service (preferably require('./package.json').version)
 * @param {String} port - Port this registration is bound to that clients can call
 */

Client.prototype.unregister = function(name, version, port) {
  var self = this;
  var host = self.MY_IP;
  var reg = registrations.create({
    name: name,
    version: version,
    host: host,
    port: port,
  });

  //
  // filter out the unwanted registration intent
  //
  self.intents = self.intents.filter(function (intent) {
    return reg.id !== intent.id;
  });

  //
  // explicitely delete the registration so the offline notifications happen immediately
  //
  self.del(name, version, host, port);
};

/**
 * Explicitly delete a registration
 *
 * @param {String} name - Name of the service
 * @param {String} version - Version of the service
 * @param {String} port - Port of the registration
 * @param {String} host - Host (ip) of the registration
 */
Client.prototype.del = function (name, version, host, port, cb) {
  var self = this;
  var uri = util.format('http://%s:%s/registrations/%s/%s/%s/%s', self.HOST, self.APIPORT, name, version, host, port);
  request({
    uri: uri,
    json: true,
    method: 'DELETE'
  },
  function (error, response, body) {
    if (error) self.log('error', 'Thalassa:Client.del', error);
    if (response && response.statusCode !== 200 && response.statusCode !== 404) {
      self.log('error', 'Thalassa:Client.del unexpected response ' + response.statusCode);
      error = new Error("del unexpected response " + response.statusCode);
    }
    if (typeof cb === 'function') cb(error);
  });
};

/**
 * Start polling, periodically checking the registrations into the Thalassa server
 */

Client.prototype.start = function() {
  var self = this;
  if (!self.isOn) {
    self.isOn = true;
    self._startUpdateInterval();
  }
};

/**
 * Stop polling registrations
 */

Client.prototype.stop = function() {
  var self = this;
  self.isOn = false;
  clearInterval(self._updateInterval);
  Object.keys(self.registrations).forEach(function (key) {
    var reg = self.registrations[key];
    delete self.registrations[key];
    self.unregister(reg.name,reg.version,reg.port);
  });
};

/**
 * Close: stop polling, disconnect socket
 */

Client.prototype.close = function() {
  var self = this;
  this.stop();
  if (this.socket) this.socket.close();
};

/**
 * Find all `Registration`s over HTTP for `name` and `version` if provided.
 *
 * @param {String} [name]
 * @param {String} [version]
 * @param {getRegistrationsCallback} cb - Callback cb(error, registrations)
 *
 * @callback getRegistrationsCallback
 * @param {Error} error
 * @param {Registrations[]} registrations
 */

Client.prototype.getRegistrations = function(name, version, cb) {
  var self = this;

  var path;
  if (typeof name === 'function') {
    cb = name;
    path = '/registrations';
  }
  else if (typeof version === 'function') {
    cb = version;
    path = util.format('/registrations/%s', name);
  }
  else if (typeof cb === 'function') {
    path = util.format('/registrations/%s/%s', name, version);
  }

  var uri = util.format('http://%s:%s%s', self.HOST, self.APIPORT, path);

  self.log('debug', 'Thalassa:Client.getRegistrations uri: ' + uri);

  request({
    uri: uri,
    json: true
  },
  function (error, response, body) {
    if (error) self.log('error', 'Thalassa:Client.getRegistrations', error);
    if (response && response.statusCode !== 200 && response.statusCode !== 404) {
      self.log('error', 'Thalassa:Client.getRegistrations unexpected response ' + response.statusCode);
      error = new Error("getRegistrations unexpected response " + response.statusCode);
    }
    if (error) return cb(error);

    var regs = (response.statusCode !== 200) ? [] : body;
    cb(null, regs);
  });
};

//
// Axon socket functions
//

/**
 * Subscribe to `offline` and `online` events over the axon socket. Connects if not
 * already connected.
 *
 * @param {String} [name]
 * @param {String} [version]
 */

 Client.prototype.subscribe = function(name, version) {
  if (this.socket === null) this.socketConnect();
  var prefix = this._keySearch(name, version);
  this.log('debug', 'Thalassa:Client.subscribe ' + prefix);
  this.socket.subscribe(prefix);
};

/**
 * Unsubscribe to `offline` and `online` events over the axon socket. Connects if not
 * already connected.
 *
 * @param {String} [name]
 * @param {String} [version]
 */

Client.prototype.unsubscribe = function(name, version) {
  if (this.socket === null) this.socketConnect();
  var prefix = this._keySearch(name, version);
  this.log('info', 'Thalassa:Client.unsubscribe ' + prefix);
  this.socket.unsubscribe(prefix);
};

/**
 * Connect to the Thalassa Server axon socket if not already connected, typically called
 * the first time `subscribe` is called
 */
Client.prototype.socketConnect = function() {
  var self = this;
  if (this.socket === null) {
    this.socket = axon.socket('sub');
    this.socket.set('identity', this.MY_IP + ':' + this.PORT);
    self.log('info', 'Thalassa:Client.socketConnect: connecting to socket ' + this.HOST + ':' + this.PORT);
    this.socket.connect(this.PORT, this.HOST);

    this.socket.on('message', function (regId, state, reg) {
      regId = regId.toString();
      state = state.toString();

      if (state === 'online') {
        var updatedReg = JSON.parse(reg);
        if(regId in self.registrations){
          //if we already have the registration, and no metadata changed, do nothing
          if(deepEqual(updatedReg.meta, self.registrations[regId].meta))
            return;
        }
        self.registrations[regId] = updatedReg;
        self.emit('online', registrations.parse(reg.toString()));
      }
      else if (state === 'offline') {
        delete registrations[regId];
        self.emit('offline', regId);
      }
      else {
        self.log('info', 'Thalassa:Client.onMessage: ignoring message, unknown state ', arguments.map(function (b) { b.toString(); }));
      }
    });
  }
};

Client.prototype._keySearch = function(name, version) {
  var keySearch = (name) ? ('/' + name) : '';
  keySearch += (version) ? util.format('/%s/*', version) : '/*';
  return keySearch;
};

Client.prototype._startUpdateInterval = function() {
  var self = this;
  update();
  self._updateInterval = setInterval(update, self.UPDATE_FREQ);

  function update () {
    self.intents.forEach(self._sendUpdate.bind(self));
  }
};

Client.prototype._sendUpdate = function (intent) {
  if (this.MODE === 'http') {
    this._sendHTTPUpdate(intent);
  }
  else {
    this.log('error', 'Thalassa:Client._sendUpdate: unsupported mode ' + this.mode);
  }
};

Client.prototype._sendHTTPUpdate = function (intent) {
  // TODO batch multiple requests?
  var self = this;
  var uri = util.format('http://%s:%s/registrations/%s/%s/%s/%s', self.HOST, self.APIPORT, intent.name, intent.version, intent.host, intent.port);
  var startTime = Date.now();

  if(!(intent.id in self.attempts) || self.attempts[intent.id] == self.ATTEMPTS_BEFORE_RETRY){
    //If this is a case of hitting max attempts, log it
    if(intent.id in self.attempts){
      self.log('error', 'Reached max number of attempts (' + self.ATTEMPTS_BEFORE_RETRY + ') waiting for response, sending new request');
    }
    self.attempts[intent.id] = 0;
  }

  // If we have a request still pending, increment the number of attempts and return
  if (self.attempts[intent.id] > 0) {
    self.attempts[intent.id]++;
    self.log('error', 'Thalassa:Client._sendHTTPUpdate call still pending! (skipping): ' + intent.id);
    return;
  }

  self.attempts[intent.id]++;

  request({
    uri: uri,
    method: 'POST',
    json: intent.meta,
    timeout: self.TIMEOUT
  },
  function (error, response, body) {
    self.attempts[intent.id] = 0;

    // Check for an unsuccessful response, in case, for example we call the wrong
    // host and it returns a 404, etc.
    if (!error && response.statusCode !== 200) {
      error = new Error(util.format('unexpected response statusCode %s from %s',
                        response.statusCode, uri));
    }

    if (error) {
      self.log('error', 'Thalassa:Client._sendUpdate', error);
      self.emit('updateFailed', error);
    }
    else {
      self.emit('updateSuccessful');
      self.log('debug', util.format('Thalassa:Client._sendUpdate (%s) [%s] %s', response.statusCode, Date.now() - startTime, uri));
    }
  });
};
<|endoftext|>


<commit_before>IronRouter = Utils.extend(IronRouter, {
  constructor: function (options) {
    var self = this;

    IronRouter.__super__.constructor.apply(this, arguments);
    self.options.linkSelector = self.options.linkSelector || 'a[href]';

    this.isRendered = false;

    /**
     * The current RouteController instance. This is set anytime a new route is
     * dispatched. It's a reactive variable which you can get by calling
     * Router.current();
     *
     * @api private
     */
    this._currentController = null;

    /**
     * Dependency to for this._currentController
     *
     * @api private
     */
    this._controllerDep = new Deps.Dependency;

    /**
      * Did the URL we are looking at come from a hot-code-reload
      *  (and thus should we treat is as not new?)
      *
      * @api private
      */
    this._hasJustReloaded = false;

    Meteor.startup(function () {
      Meteor.defer(function () {
        if (self.options.autoRender !== false)
          self.autoRender();
        if (self.options.autoStart !== false)
          self.start();
      });
    });

    // proxy these methods to the underlying ui manager object
    _.each([
      'layout',
      'setRegion',
      'clearRegion',
      'getData',
      'setData'
    ], function (uiApiMethod) {
      self[uiApiMethod] = function () {
        if (!self._ui)
          throw new Error("No uiManager is configured on the Router");
        return self._ui[uiApiMethod].apply(self._ui, arguments);
      };
    });
  },

  configure: function (options) {
    options = options || {};

    IronRouter.__super__.configure.apply(this, arguments);

    if (options.uiManager && this.isRendered)
      throw new Error("Can't set uiManager after Router has been rendered");
    else if (options.uiManager) {
      this._ui = options.uiManager;
    }

    return this;
  },

  /**
   * Reactive accessor for the current RouteController instance. You can also
   * get a nonreactive value by specifiying {reactive: false} as an option.
   *
   * @param {Object} [opts] configuration options
   * @param {Boolean} [opts.reactive] Set to false to enable a non-reactive read.
   * @return {RouteController}
   * @api public
   */

  current: function (opts) {
    if (opts && opts.reactive === false)
      return this._currentController;
    else {
      this._controllerDep.depend();
      return this._currentController;
    }
  },

  clearUnusedRegions: function (usedYields) {
    if (!this._ui)
      throw new Error('No ui manager has been set');

    var self = this;
    var layout = this._ui;

    var allYields = layout.getRegionKeys();
    usedYields = _.filter(usedYields, function (val) {
      return !!val;
    });

    var unusedYields = _.difference(allYields, usedYields);

    _.each(unusedYields, function (key) {
      layout.clearRegion(key);
    });
  },

  run: function (controller, cb) {
    IronRouter.__super__.run.apply(this, arguments);

    if (controller == this._currentController) {
      this._controllerDep.changed();
    }
  },

  /**
   * Wrapper around Location.go that accepts a routeName or a path as the first
   * parameter. This method can accept client and server side routes.
   *
   * Examples:
   *
   *  1. Router.go('/posts', {state: 'true'});
   *  2. Router.go('postIndex', [param1, param2], {state});
   *
   * @param {String} routeNameOrPath
   * @param {Array|Object} [params]
   * @param {Object} [state]
   * @param {Boolean} [replaceState]
   * @api public
   */

  go: function (routeNameOrPath, params, options) {
    var self = this;
    var isPathRe = /^\/|http/
    var route;
    var path;
    var onComplete;
    var controller;
    var done;

    // after the dispatch is complete, set the IronLocation
    // path and state which will update the browser's url.
    done = function() {
      options = options || {};
      self._location.set(path, {
        replaceState: options.replaceState,
        state: options.state,
        skipReactive: true
      });
    };

    if (isPathRe.test(routeNameOrPath)) {
      path = routeNameOrPath;
      options = params;

      // if the path hasn't changed (at all), we are going to do nothing here
      if (path === self._location.path()) {
        if (self.options.debug)
          console.log("You've navigated to the same path that you are currently at. Doing nothing");
        return;
      }

      // issue here is in the dispatch process we might want to
      // make a server request so therefore not call this method yet, so
      // we need to push the state only after we've decided it's a client
      // request, otherwise let the browser handle it and send off to the
      // server
      self.dispatch(path, options, done);
    } else {
      route = self.routes[routeNameOrPath];
      Utils.assert(route, 'No route found named ' + routeNameOrPath);
      path = route.path(params, options);
      controller = route.newController(path, options);
      self.run(controller, done);
    }
  },

  render: function () {
    //XXX this probably needs to be reworked since _ui.render() returns an
    //inited component which doesnt work with Shark rendering pipeline.
    if (!this._ui)
      throw new Error("No uiManager configured on Router");
    this.isRendered = true;
    return this._ui.render();
  },

  autoRender: function () {
    if (!this._ui)
      throw new Error("No uiManager configured on Router");
    this._ui.insert(document.body, UI.body, {
      template: this.options.layoutTemplate
    });
  },

  bindEvents: function () {
    $(document).on('click.ironRouter', this.options.linkSelector, _.bind(this.onClick, this));
  },

  unbindEvents: function () {
    $(document).off('click.ironRouter', this.options.linkSelector);
  },

  /**
   * Start listening to click events and set up a Deps.autorun for location
   * changes. If already started the method just returns.
   *
   * @api public
   */

  start: function () {
    var self = this;

    if (self.isStarted) return;

    self.isStarted = true;

    self._location = self.options.location || IronLocation;
    self._location.start();

    self.bindEvents();

    Deps.autorun(function (c) {
      var location;
      self._locationComputation = c;
      self.dispatch(self._location.path(), {state: history.state});
    });
  },

  /**
   * Remove click event listener and stop listening for location changes.
   *
   * @api public
   */

  stop: function () {
    this.isStarted = false;

    this.unbindEvents();
    this._location.stop();

    if (this._locationComputation)
      this._locationComputation.stop();
  },

  /**
   * If we don't handle a link but the server does, bail to the server
   *
   * @api public
   */
  onUnhandled: function (path, options) {
    this.stop();
    window.location = path;
  },

  /**
   * if we don't handle a link, _and_ the  server doesn't handle it,
   * do one of two things:
   *   a) if this is the initial route, then it can't be a static asset, so
   *      show notFound or throw an error
   *   b) otherwise, let the server have a go at it, we may end up coming back.
   *
   * @api public
   */
  onRouteNotFound: function (path, options) {
    if (this._location.path() !== path) {
      this.stop();
      window.location = path;
    } else if (this.options.notFoundTemplate) {
      var notFoundRoute = new Route(this, '__notfound__', _.extend(options || {}, {path: path}));
      this.run(new RouteController(this, notFoundRoute, {
        layoutTemplate: this.options.layoutTemplate,
        template: this.options.notFoundTemplate
      }));
    } else {
      throw new Error('Oh no! No route found for path: "' + path + '"');
    }
  },

  onClick: function(e) {
    var el = e.currentTarget;
    var which = _.isUndefined(e.which) ? e.button : e.which;
    var href = el.href;
    var path = el.pathname + el.search + el.hash;

    // we only want to handle clicks on links which:
    // - haven't been cancelled already
    if (e.isDefaultPrevented())
      return;

    //  - are with the left mouse button with no meta key pressed
    if (which !== 1)
      return;

    if (e.metaKey || e.ctrlKey || e.shiftKey)
      return;

    // - aren't in a new window
    if (el.target)
      return;

    // - aren't external to the app
    if (!IronLocation.isSameOrigin(href))
      return;

    // note that we _do_ handle links which point to the current URL
    // and links which only change the hash.
    e.preventDefault();
    this.go(path);
  }
});

/**
 * The main Router instance that clients will deal with
 *
 * @api public
 * @exports Router
 */

Router = new IronRouter;

if (Meteor._reload) {
  // just register the fact that a migration _has_ happened
  Meteor._reload.onMigrate('iron-router', function() { return [true, true]});

  // then when we come back up, check if it it's set
  var data = Meteor._reload.migrationData('iron-router');
  Router._hasJustReloaded = data;
}
<commit_msg>Don’t call unnecessary stop() in client router when browsing away.<commit_after>IronRouter = Utils.extend(IronRouter, {
  constructor: function (options) {
    var self = this;

    IronRouter.__super__.constructor.apply(this, arguments);
    self.options.linkSelector = self.options.linkSelector || 'a[href]';

    this.isRendered = false;

    /**
     * The current RouteController instance. This is set anytime a new route is
     * dispatched. It's a reactive variable which you can get by calling
     * Router.current();
     *
     * @api private
     */
    this._currentController = null;

    /**
     * Dependency to for this._currentController
     *
     * @api private
     */
    this._controllerDep = new Deps.Dependency;

    /**
      * Did the URL we are looking at come from a hot-code-reload
      *  (and thus should we treat is as not new?)
      *
      * @api private
      */
    this._hasJustReloaded = false;

    Meteor.startup(function () {
      Meteor.defer(function () {
        if (self.options.autoRender !== false)
          self.autoRender();
        if (self.options.autoStart !== false)
          self.start();
      });
    });

    // proxy these methods to the underlying ui manager object
    _.each([
      'layout',
      'setRegion',
      'clearRegion',
      'getData',
      'setData'
    ], function (uiApiMethod) {
      self[uiApiMethod] = function () {
        if (!self._ui)
          throw new Error("No uiManager is configured on the Router");
        return self._ui[uiApiMethod].apply(self._ui, arguments);
      };
    });
  },

  configure: function (options) {
    options = options || {};

    IronRouter.__super__.configure.apply(this, arguments);

    if (options.uiManager && this.isRendered)
      throw new Error("Can't set uiManager after Router has been rendered");
    else if (options.uiManager) {
      this._ui = options.uiManager;
    }

    return this;
  },

  /**
   * Reactive accessor for the current RouteController instance. You can also
   * get a nonreactive value by specifiying {reactive: false} as an option.
   *
   * @param {Object} [opts] configuration options
   * @param {Boolean} [opts.reactive] Set to false to enable a non-reactive read.
   * @return {RouteController}
   * @api public
   */

  current: function (opts) {
    if (opts && opts.reactive === false)
      return this._currentController;
    else {
      this._controllerDep.depend();
      return this._currentController;
    }
  },

  clearUnusedRegions: function (usedYields) {
    if (!this._ui)
      throw new Error('No ui manager has been set');

    var self = this;
    var layout = this._ui;

    var allYields = layout.getRegionKeys();
    usedYields = _.filter(usedYields, function (val) {
      return !!val;
    });

    var unusedYields = _.difference(allYields, usedYields);

    _.each(unusedYields, function (key) {
      layout.clearRegion(key);
    });
  },

  run: function (controller, cb) {
    IronRouter.__super__.run.apply(this, arguments);

    if (controller == this._currentController) {
      this._controllerDep.changed();
    }
  },

  /**
   * Wrapper around Location.go that accepts a routeName or a path as the first
   * parameter. This method can accept client and server side routes.
   *
   * Examples:
   *
   *  1. Router.go('/posts', {state: 'true'});
   *  2. Router.go('postIndex', [param1, param2], {state});
   *
   * @param {String} routeNameOrPath
   * @param {Array|Object} [params]
   * @param {Object} [state]
   * @param {Boolean} [replaceState]
   * @api public
   */

  go: function (routeNameOrPath, params, options) {
    var self = this;
    var isPathRe = /^\/|http/
    var route;
    var path;
    var onComplete;
    var controller;
    var done;

    // after the dispatch is complete, set the IronLocation
    // path and state which will update the browser's url.
    done = function() {
      options = options || {};
      self._location.set(path, {
        replaceState: options.replaceState,
        state: options.state,
        skipReactive: true
      });
    };

    if (isPathRe.test(routeNameOrPath)) {
      path = routeNameOrPath;
      options = params;

      // if the path hasn't changed (at all), we are going to do nothing here
      if (path === self._location.path()) {
        if (self.options.debug)
          console.log("You've navigated to the same path that you are currently at. Doing nothing");
        return;
      }

      // issue here is in the dispatch process we might want to
      // make a server request so therefore not call this method yet, so
      // we need to push the state only after we've decided it's a client
      // request, otherwise let the browser handle it and send off to the
      // server
      self.dispatch(path, options, done);
    } else {
      route = self.routes[routeNameOrPath];
      Utils.assert(route, 'No route found named ' + routeNameOrPath);
      path = route.path(params, options);
      controller = route.newController(path, options);
      self.run(controller, done);
    }
  },

  render: function () {
    //XXX this probably needs to be reworked since _ui.render() returns an
    //inited component which doesnt work with Shark rendering pipeline.
    if (!this._ui)
      throw new Error("No uiManager configured on Router");
    this.isRendered = true;
    return this._ui.render();
  },

  autoRender: function () {
    if (!this._ui)
      throw new Error("No uiManager configured on Router");
    this._ui.insert(document.body, UI.body, {
      template: this.options.layoutTemplate
    });
  },

  bindEvents: function () {
    $(document).on('click.ironRouter', this.options.linkSelector, _.bind(this.onClick, this));
  },

  unbindEvents: function () {
    $(document).off('click.ironRouter', this.options.linkSelector);
  },

  /**
   * Start listening to click events and set up a Deps.autorun for location
   * changes. If already started the method just returns.
   *
   * @api public
   */

  start: function () {
    var self = this;

    if (self.isStarted) return;

    self.isStarted = true;

    self._location = self.options.location || IronLocation;
    self._location.start();

    self.bindEvents();

    Deps.autorun(function (c) {
      var location;
      self._locationComputation = c;
      self.dispatch(self._location.path(), {state: history.state});
    });
  },

  /**
   * Remove click event listener and stop listening for location changes.
   *
   * @api public
   */

  stop: function () {
    this.isStarted = false;

    this.unbindEvents();
    this._location.stop();

    if (this._locationComputation)
      this._locationComputation.stop();
  },

  /**
   * If we don't handle a link but the server does, bail to the server
   *
   * @api public
   */
  onUnhandled: function (path, options) {
    window.location = path;
  },

  /**
   * if we don't handle a link, _and_ the  server doesn't handle it,
   * do one of two things:
   *   a) if this is the initial route, then it can't be a static asset, so
   *      show notFound or throw an error
   *   b) otherwise, let the server have a go at it, we may end up coming back.
   *
   * @api public
   */
  onRouteNotFound: function (path, options) {
    if (this._location.path() !== path) {
      this.stop();
      window.location = path;
    } else if (this.options.notFoundTemplate) {
      var notFoundRoute = new Route(this, '__notfound__', _.extend(options || {}, {path: path}));
      this.run(new RouteController(this, notFoundRoute, {
        layoutTemplate: this.options.layoutTemplate,
        template: this.options.notFoundTemplate
      }));
    } else {
      throw new Error('Oh no! No route found for path: "' + path + '"');
    }
  },

  onClick: function(e) {
    var el = e.currentTarget;
    var which = _.isUndefined(e.which) ? e.button : e.which;
    var href = el.href;
    var path = el.pathname + el.search + el.hash;

    // we only want to handle clicks on links which:
    // - haven't been cancelled already
    if (e.isDefaultPrevented())
      return;

    //  - are with the left mouse button with no meta key pressed
    if (which !== 1)
      return;

    if (e.metaKey || e.ctrlKey || e.shiftKey)
      return;

    // - aren't in a new window
    if (el.target)
      return;

    // - aren't external to the app
    if (!IronLocation.isSameOrigin(href))
      return;

    // note that we _do_ handle links which point to the current URL
    // and links which only change the hash.
    e.preventDefault();
    this.go(path);
  }
});

/**
 * The main Router instance that clients will deal with
 *
 * @api public
 * @exports Router
 */

Router = new IronRouter;

if (Meteor._reload) {
  // just register the fact that a migration _has_ happened
  Meteor._reload.onMigrate('iron-router', function() { return [true, true]});

  // then when we come back up, check if it it's set
  var data = Meteor._reload.migrationData('iron-router');
  Router._hasJustReloaded = data;
}
<|endoftext|>


<commit_before>var CloudCommander, CloudFunc, $;
/* object contains viewer FancyBox
 * https://github.com/fancyapps/fancyBox
 */
CloudCommander.Viewer = {getByClass  : function(pClass){
        return document.getElementsByClassName(pClass);
    }};
CloudCommander.Viewer.FancyBox = {
    load: function(){
        var ljsLoad_f = function(){
                CloudCommander.jsload(
                    'http://fancyapps.com/fancybox/source/jquery.fancybox.pack.js');
        };

        CloudCommander.cssLoad({
            src  : 'http://fancyapps.com/fancybox/source/jquery.fancybox.css',
            func : ljsLoad_f
        });
    },
    set: function(){
        if(this.getByClass('fancybox').length)
            return;
        try{
            /* get current panel (left or right) */
            var lPanel = this.getByClass('current-file');

            lPanel.length &&
                (lPanel = lPanel[0].parentElement);

            /* get all file links */
            var lA = lPanel.getElementsByTagName('a');

            var lName;
            for (var i=0; i < lA.length; i++) {
                lName = lA[i].title || lA[i].textContent;

                CloudFunc.checkExtension(lName,['png','jpg']) &&
                    (lA[i].className    = 'fancybox') &&
                    (lA[i].rel          = 'gallery');
            }
        }catch(pError){
            console.log(pError);
        }
    },
    getById     : function(pId){return document.getElementById(pId);},
    getByClass  : function(pClass){
        return document.getElementsByClassName(pClass);
    }
};
CloudCommander.Viewer.Keys = (function(){
    "use strict";

    CloudCommander.Viewer.FancyBox.load();

    var key_event = function(pParent){
        return function(event){
            /* если клавиши можно обрабатывать */
            if(CloudCommander.keyBinded){
                /* if f4 pressed */
                if(event.keyCode===114){
                    CloudCommander.Viewer.FancyBox.set();
                    var lCurrent = pParent.getByClass('current-file');
                          lCurrent.length &&
                            (lCurrent = lCurrent[0]);

                    var lA = lCurrent.getElementsByClassName('fancybox');

                    lA.length &&
                    $.fancybox.open({ href : lA[0].href },{
                        helpers : {
                            overlay : {
                                opacity: 0.1,
                                css : {
                                    'background-color' : '#fff'
                                }
                            }
                        },
                        padding : 0
                    });

                event.preventDefault();
                }
            }
        };
    };

    /* добавляем обработчик клавишь */
    if (document.addEventListener)
        document.addEventListener('keydown', key_event(this),false);

    else
        document.onkeypress=key_event;

    /* клавиши назначены*/
    CloudCommander.keyBinded=true;
});<commit_msg>minor changes<commit_after>var CloudCommander, CloudFunc, $;
/* object contains viewer FancyBox
 * https://github.com/fancyapps/fancyBox
 */
CloudCommander.Viewer = {getByClass  : function(pClass){
        return document.getElementsByClassName(pClass);
    }};
CloudCommander.Viewer.FancyBox = {
    load: function(){
        var ljsLoad_f = function(){
                CloudCommander.jsload(
                    'http://fancyapps.com/fancybox/source/jquery.fancybox.pack.js');
        };

        CloudCommander.cssLoad({
            src  : 'http://fancyapps.com/fancybox/source/jquery.fancybox.css',
            func : ljsLoad_f
        });
    },
    set: function(){
        if(this.getByClass('fancybox').length)
            return;
        try{
            /* get current panel (left or right) */
            var lPanel = this.getByClass('current-file');

            lPanel.length &&
                (lPanel = lPanel[0].parentElement);

            /* get all file links */
            var lA = lPanel.getElementsByTagName('a');

            var lName;
            for (var i=0; i < lA.length; i++) {
                lName = lA[i].title || lA[i].textContent;

                CloudFunc.checkExtension(lName,['png','jpg']) &&
                    (lA[i].className    = 'fancybox') &&
                    (lA[i].rel          = 'gallery');
            }
        }catch(pError){
            console.log(pError);
        }
    },
    getById     : function(pId){return document.getElementById(pId);},
    getByClass  : function(pClass){
        return document.getElementsByClassName(pClass);
    }
};
CloudCommander.Viewer.Keys = (function(){
    "use strict";

    CloudCommander.Viewer.FancyBox.load();

    var key_event = function(pParent){
        return function(event){
            /* если клавиши можно обрабатывать */
            if(CloudCommander.keyBinded){
                /* if f4 pressed */
                if(event.keyCode===114){
                    CloudCommander.keyBinded = false;

                    CloudCommander.Viewer.FancyBox.set();
                    var lCurrent = pParent.getByClass('current-file');
                          lCurrent.length &&
                            (lCurrent = lCurrent[0]);

                    var lA = lCurrent.getElementsByClassName('fancybox');

                    lA.length &&
                    $.fancybox.open({ href : lA[0].href },{
                        beforeClose: (function(){
                            CloudCommander.keyBinded = true;
                        }),
                        helpers : {
                            overlay : {
                                opacity: 0.1,
                                css : {
                                    'background-color' : '#fff'
                                }
                            }
                        },
                        padding : 0
                    });

                event.preventDefault();
                }
            }
        };
    };

    /* добавляем обработчик клавишь */
    if (document.addEventListener)
        document.addEventListener('keydown', key_event(this),false);

    else
        document.onkeypress=key_event;

    /* клавиши назначены*/
    CloudCommander.keyBinded=true;
});<|endoftext|>


<commit_before>const S3 = require('aws-sdk/clients/s3');
const BackbeatClient = require('./BackbeatClient');

function attachReqUids(s3req, log) {
    s3req.on('build', () => {
        // eslint-disable-next-line no-param-reassign
        s3req.httpRequest.headers['X-Scal-Request-Uids'] =
            log.getSerializedUids();
    });
}

function createS3Client(params) {
    const { transport, host, port, credentials, agent } = params;
    return new S3({
        endpoint: `${transport}://${host}:${port}`,
        credentials,
        sslEnabled: transport === 'https',
        s3ForcePathStyle: true,
        signatureVersion: 'v4',
        httpOptions: { agent, timeout: 0 },
        maxRetries: 0,
    });
}

function createBackbeatClient(params) {
    const { transport, host, port, credentials, agent } = params;
    const endpoint = `${transport}://${host}:${port}`;
    return new BackbeatClient({
        endpoint,
        credentials,
        sslEnabled: transport === 'https',
        httpOptions: { agent, timeout: 0 },
        maxRetries: 0,
    });
}

module.exports = {
    attachReqUids,
    createS3Client,
    createBackbeatClient,
};
<commit_msg>BB-257 Backbeat client requests never time out<commit_after>const S3 = require('aws-sdk/clients/s3');
const BackbeatClient = require('./BackbeatClient');

const TIMEOUT_MS = 1000 * 60 * 2; // 2 minutes in ms

function attachReqUids(s3req, log) {
    s3req.on('build', () => {
        // eslint-disable-next-line no-param-reassign
        s3req.httpRequest.headers['X-Scal-Request-Uids'] =
            log.getSerializedUids();
    });
}

function createS3Client(params) {
    const { transport, host, port, credentials, agent } = params;
    return new S3({
        endpoint: `${transport}://${host}:${port}`,
        credentials,
        sslEnabled: transport === 'https',
        s3ForcePathStyle: true,
        signatureVersion: 'v4',
        httpOptions: { agent, timeout: 0 },
        maxRetries: 0,
    });
}

function createBackbeatClient(params) {
    const { transport, host, port, credentials, agent } = params;
    const endpoint = `${transport}://${host}:${port}`;
    return new BackbeatClient({
        endpoint,
        credentials,
        sslEnabled: transport === 'https',
        httpOptions: { agent, timeout: TIMEOUT_MS },
        maxRetries: 0,
    });
}

module.exports = {
    attachReqUids,
    createS3Client,
    createBackbeatClient,
};
<|endoftext|>


<commit_before> 'use strict';

var build       = module.exports = {};
var fs          = require('fs');
var fse         = require('fs-extra');
var expand      = require('fs-expand');
var node_path   = require('path');
var semver      = require('semver');
var stable      = require('semver-stable');
var async       = require('async');
var cortex_json   = require('read-cortex-json');
var semver_helper = require('../util/semver');
var lang        = require('../util/lang');
var spawns      = require('spawns');
var builder     = require('neuron-builder');

// @param {Object} options
//      see ./lib/option/build.js for details
build.run = function(options, callback) {
  var profile = this.profile;

  // if the destination is not specified by user
  if (!options.dest) {
    var entry_cwd = options['entry-cwd'];

    if (!profile.get('server_mode') && entry_cwd) {
      // if cortex is not running in server mode, see 'doc/<language>/server-mode.md',
      // we will build module into <root-cwd>/neurons folder
      options.dest = node_path.join(entry_cwd, 'neurons');

    } else {
      // If server_mode is on, we assume that we always use cortex server to develop,
      // and all packages will be built to a certain dir.
      options.dest = profile.get('built_root');
    }
  }

  this.MESSAGE = this.locale.require('command-build');

  var self = this;
  async.series([

    function(done) {
      self.read_package_json(options, done);
    },

    function(done) {
      self.run_preinstall_script(options, done);
    },

    function(done) {
      // run custom build script
      self.run_prebuild_script(options, done);
    },

    function(done) {
      // determine build tasks
      self.determine_tasks(options, done);
    },

    function(done) {
      // real building
      self.build(options, done);
    }

  ], function(err) {
    callback(err);
  });
};

build.read_package_json = function(options, callback) {
  cortex_json.read(options.cwd, function(err, pkg) {
    options.pkg = pkg;
    callback(err);
  });
};

// Run custom build scripts, such as 'grunt'
build.run_preinstall_script = function(options, callback) {
  if (!options.preinstall) {
    return callback(null);
  }

  this.run_script('preinstall', options, callback);
};

// Run custom build scripts, such as 'grunt'
build.run_prebuild_script = function(options, callback) {
  if (!options.prebuild) {
    return callback(null);
  }

  this.run_script('prebuild', options, callback);
};

build.run_script = function(script, options, callback) {
  var pkg = options.pkg;
  var scripts = lang.makeArray(pkg.scripts && pkg.scripts[script])
  // skip empty scripts
  .filter(Boolean);

  if (!scripts.length) {
    return callback(null);
  }

  var self = this;

  this.logger.info('{{cyan run}} "scripts.' + script + '" ...');

  spawns(scripts, {
    cwd: options.cwd,
    stdio: 'inherit'

  }).on('spawn', function(command) {
    self.logger.info(' - {{cyan exec}} "' + command + '" ...');

  }).on('close', function(code, signal) {
    if (code) {
      callback({
        code: 'EBUILDSCRIPT',
        message: 'build step "scripts.' + script + '" executes as a failure. exit code: ' + code,
        data: {
          code: code,
          signal: signal,
          script: script
        }
      });
    } else {
      callback(null);
    }

  }).on('error', function(err) {
    self.logger.warn('"scripts.' + script + '" let out an error: ' + err);
  });
};

build.determine_tasks = function(options, callback) {
  var cache_root = this.profile.get('cache_root');
  var tasks = options.tasks = {};
  var name = options.pkg.name;
  var version = options.pkg.version;
  var built_root = options.dest;

  var parsed = semver_helper.parse(version);

  if (!parsed) {
    return callback({
      code: 'EINVALIDVER',
      message: 'invalid semantic version "' + version + '" in package.json, see http://semver.org for details.',
      data: {
        version: version
      }
    });
  }

  // Cortex will no longer publish prerelease versions into the registry,
  // so use the original version directly.
  tasks[version] = node_path.join(built_root, name, version);

  var self = this;

  if (options.ranges) {
    cortex_json.cached_document({
      name: name,
      cache_root: cache_root

    }, function(err, json) {
      if (err) {
        return callback(err);
      }

      var versions = json && json.versions && Object.keys(json.versions) || [];
      versions.sort(semver.rcompare);

      // '1.2.3' -> '~1.2.0'
      var range = semver_helper.get_base_range(parsed);
      var max_patch = semver.maxSatisfying(versions, range);
      var max_stable_patch = stable.maxSatisfying(versions, range);
      var max = versions[0];
      var max_stable = stable.max(versions);

      if (
        // [1]
        // If the current minor(feature) doesn't exist, it is the max matched patch of the current minor
        !max_patch ||

        // Greater or equal to
        semver.gte(version, max_patch) ||

        // If the current version is greater or equal to the max stable patch,
        // we also let it go
        max_stable_patch && semver.gte(version, max_stable_patch)
      ) {
        // >>>> range
        tasks[range] = node_path.join(built_root, name, range);

        // if the package is not the latest patch,
        // it's impossible to be the latest of all versions
        if (
          // if there's no versions on the registry,
          // See 1
          !max ||

          // if current version is the greatest
          semver.gte(version, max) ||
          max_stable && semver.gte(version, max_stable)
        ) {
          // >>>> latest
          tasks.latest = node_path.join(built_root, name, 'latest');
        }
      }

      // {
      //     '1.2.3': '/xxx/~1.2.0',
      //     'latest': '/xxx/latest'
      // }
      callback(null, tasks);
    });

  } else {
    callback(null, tasks);
  }
};

build.build = function(options, callback) {
  var pkg = options.pkg;
  // distribution directory
  var dist = lang.object_member_by_namespaces(options.pkg, 'directories.dist');

  if (dist) {
    return this.check_dist(dist, options, callback);
  }

  var self = this;
  async.parallel([
    function (done) {
      self.build_modules();
    },

    function (done) {
      self.build_css();
    },

    function (done) {
      self.copy_directories();
    }
  ], callback);
};

build.build_modules = function () {
  self.get_entries(options, function(err) {
    if (err) {
      return callback(err);
    }

    var tasks = options.tasks;

    async.eachSeries(Object.keys(tasks), function(target_version, done) {
      var to = tasks[target_version];
      self.run_build_task(target_version, to, options, done);
    }, callback);
  });
};

build.check_dist = function (dist, options, callback) {
  dist = node_path.join(options.cwd, dist);

  // if `dist` dir exists, skip building and just copy it
  fs.exists(dist, function(exists) {
    if (!exists) {
      // if `cortex.directories.dist` is declared, the dir must be existed.
      return callback({
        code: 'EDISTNOTEXISTS',
        message: 'dist dir "' + dist + '" does not exist.',
        data: {
          dist: dist
        }
      });
    }

    this.logger.info('dist dir "' + dist + '" found, {{cyan skip}} building ...');
    this.copy_dist(dist, options, callback);
  }.bind(this));
};

// copy dist dir to the destination dirs
build.copy_dist = function(dist, options, callback) {
  var self = this;
  var tasks = options.tasks;
  async.each(Object.keys(tasks), function(task, done) {
    var to = tasks[task];
    self.logger.info('{{cyan copy}} "' + dist + '" to "' + to + '".');
    self.cp(dist, to, done);
  }, callback);
};

build.get_entries = function(options, callback) {
  var cwd = options.cwd;
  var pkg = options.pkg;

  var main_entry = pkg.main || 'index.js';

  // Standardize main entry
  // './index' -> './index.js'
  main_entry = this.ensure_ext(main_entry, 'js');

  // './index.js' -> 'index.js'
  options.main_entry =
    main_entry =
    node_path.join('.', main_entry);

  // the module '<name>.js'
  var main_by_name = node_path.join(cwd, pkg.name) + '.js';
  var main_path = node_path.join(cwd, main_entry);

  fs.exists(main_by_name, function(exists) {
    if (exists && main_by_name !== main_path) {
      return callback({
        code: 'EMAINCONFLICT',
        message: 'if "<package.name>.js" exists, it must be `package.main`, or you should rename it.',
        data: {
          name: pkg.name,
          main: pkg.main
        }
      });
    }

    var entries = options.entries = pkg.entries ?
      expand.sync(pkg.entries, {
        cwd: cwd
      }) :
      [];

    var fail = entries.some(function(entry) {
      // entries should not be located at the parent folder of the main entry.
      if (node_path.relative(node_path.dirname(main_entry), entry).indexOf('..') === 0) {
        callback({
          code: 'EINVALIDENTRIES',
          message: 'entry "' + entry + '" should not be located at the parent folder of "' + main_entry + '"',
          data: {
            main: main_entry,
            entry: entry
          }
        });

        return true;
      }
    });

    if (!fail) {
      // prevent duplication
      if (!~entries.indexOf(main_entry)) {
        entries.push(main_entry);
      }

      callback(null);
    }
  });
};

build.ensure_ext = function(path, ext) {
  var regex = new RegExp('\\.' + ext + '$', 'i');

  if (!regex.test(path)) {
    path += '.' + ext;
  }

  return path;
};

build.run_build_task = function(target_version, to, options, callback) {
  var self = this;
  var cwd = options.cwd;
  async.each(options.entries, function (entry, done) {
    // Standardize entries
    // './index.js' -> 'index.js'
    entry = node_path.join('.', entry);
    var from = node_path.join(cwd, entry);

    builder(from, {
      cwd: cwd,
      targetVersion: target_version,
      pkg: options.pkg

    }, function (err, content) {
      if (err) {
        return done(err);
      }

      var file_to = entry === options.main_entry
        // main entry will
        ? options.pkg.name + '.js'
        : entry;

      var path_to = node_path.join(to, file_to);
      fse.mkdirs( node_path.dirname(path_to), function (err) {
        if (err) {
          return done(err);
        }

        self.logger.info('{{cyan write}} ' + path_to);
        fs.writeFile(path_to, content, done);
      });
    });

  }, function (err) {
    if (err) {
      return callback(err);
    }

    self.copy_directories(to, options, callback);
  });
};

build.copy_directories = function(to, options, callback) {
  var pkg = options.pkg;
  var directories = pkg.directories || {};

  var tasks = [
    // 'css',
    // 'template',
    'src'
  ]
  .filter(function (dir) {
    return directories[dir];
  })
  .map(function (dir) {
    return {
      src: directories[dir],
      strict: true,
      name: dir
    }
  });

  tasks.push({
    src: 'cortex-shrinkwrap.json',
    strict: false
  });

  async.each(tasks, function (task, done) {
    var src = task.src;
    if (src) {
      var source = node_path.join(options.cwd, src);
      var dest = node_path.join(to, src);
      this.cp(source, dest, function (err) {
        if (err) {
          if (err.code === 'SRC_NOT_FOUND') {
            var name = task.name;
            return done({
              code: 'DIR_NOT_FOUND',
              message: '`directories.' + name + '` is defined in cortex.json, but not found.',
              data: {
                dir: name
              }
            });
          }
        }
        done(null);
      }, task.strict);

    } else {
      done(null);
    }
  }.bind(this), callback);
};

// publish files from `from` to `to`
build.cp = function(from, to, callback, strict) {
  var self = this;
  if (from === to) {
    callback(null);
    return;
  }

  // TODO:
  // refactor with promise
  var src_exists = []
  async.each([from, to].map(function (src, index) {
    return {
      src: src,
      index: index
    }

  }), function (task, done) {
    fs.exists(task.src, function (exists) {
      src_exists[task.index] = exists;
      done(null);
    });

  }, function () {
    var from_exists = src_exists[0];
    var to_exists = src_exists[1];

    if (!from_exists) {
      // if strict and the source is not found, an error will throw.
      if (strict) {
        return callback({
          code: 'SRC_NOT_FOUND'
        });
      } else {
        return callback(null);
      }
    }

    var tasks = [];
    if (to_exists) {
      tasks.push(function(done) {
        fse.remove(to, done);
      });
    }

    tasks.push(function(done) {
      self.logger.info('{{cyan copy}} ' + from + ' -> ' + to);
      fse.copy(from, to, done);
    });

    async.series(tasks, callback);
  });
};
<commit_msg>command/build: refactors and suuports package.css; #354<commit_after> 'use strict';

var build       = module.exports = {};
var fs          = require('fs');
var fse         = require('fs-extra');
var expand      = require('fs-expand');
var node_path   = require('path');
var semver      = require('semver');
var stable      = require('semver-stable');
var async       = require('async');
var cortex_json   = require('read-cortex-json');
var semver_helper = require('../util/semver');
var lang        = require('../util/lang');
var spawns      = require('spawns');
var builder     = require('neuron-builder');

// @param {Object} options
//      see ./lib/option/build.js for details
build.run = function(options, callback) {
  var profile = this.profile;

  // if the destination is not specified by user
  if (!options.dest) {
    var entry_cwd = options['entry-cwd'];

    if (!profile.get('server_mode') && entry_cwd) {
      // if cortex is not running in server mode, see 'doc/<language>/server-mode.md',
      // we will build module into <root-cwd>/neurons folder
      options.dest = node_path.join(entry_cwd, 'neurons');

    } else {
      // If server_mode is on, we assume that we always use cortex server to develop,
      // and all packages will be built to a certain dir.
      options.dest = profile.get('built_root');
    }
  }

  this.MESSAGE = this.locale.require('command-build');

  var self = this;
  async.series([
    function(done) {
      self.read_package_json(options, done);
    },
    function(done) {
      self.run_preinstall_script(options, done);
    },
    function(done) {
      // run custom build script
      self.run_prebuild_script(options, done);
    },
    function(done) {
      // determine build tasks
      self.determine_tasks(options, done);
    },
    function(done) {
      // real building
      self.build(options, done);
    }
  ], function(err) {
    callback(err);
  });
};

build.read_package_json = function(options, callback) {
  cortex_json.read(options.cwd, function(err, pkg) {
    options.pkg = pkg;
    callback(err);
  });
};

// Run custom build scripts, such as 'grunt'
build.run_preinstall_script = function(options, callback) {
  if (!options.preinstall) {
    return callback(null);
  }

  this.run_script('preinstall', options, callback);
};

// Run custom build scripts, such as 'grunt'
build.run_prebuild_script = function(options, callback) {
  if (!options.prebuild) {
    return callback(null);
  }

  this.run_script('prebuild', options, callback);
};

build.run_script = function(script, options, callback) {
  var pkg = options.pkg;
  var scripts = lang.makeArray(pkg.scripts && pkg.scripts[script])
  // skip empty scripts
  .filter(Boolean);

  if (!scripts.length) {
    return callback(null);
  }

  var self = this;

  this.logger.info('{{cyan run}} "scripts.' + script + '" ...');

  spawns(scripts, {
    cwd: options.cwd,
    stdio: 'inherit'

  }).on('spawn', function(command) {
    self.logger.info(' - {{cyan exec}} "' + command + '" ...');

  }).on('close', function(code, signal) {
    if (code) {
      callback({
        code: 'EBUILDSCRIPT',
        message: 'build step "scripts.' + script + '" executes as a failure. exit code: ' + code,
        data: {
          code: code,
          signal: signal,
          script: script
        }
      });
    } else {
      callback(null);
    }

  }).on('error', function(err) {
    self.logger.warn('"scripts.' + script + '" let out an error: ' + err);
  });
};

build.determine_tasks = function(options, callback) {
  var cache_root = this.profile.get('cache_root');
  var tasks = options.tasks = {};
  var name = options.pkg.name;
  var version = options.pkg.version;
  var built_root = options.dest;

  var parsed = semver_helper.parse(version);

  if (!parsed) {
    return callback({
      code: 'EINVALIDVER',
      message: 'invalid semantic version "' + version + '" in package.json, see http://semver.org for details.',
      data: {
        version: version
      }
    });
  }

  // Cortex will no longer publish prerelease versions into the registry,
  // so use the original version directly.
  tasks[version] = node_path.join(built_root, name, version);

  var self = this;

  if (options.ranges) {
    cortex_json.cached_document(name, cache_root, function(err, json) {
      if (err) {
        return callback(err);
      }

      var versions = json && json.versions && Object.keys(json.versions) || [];
      versions.sort(semver.rcompare);

      // '1.2.3' -> '~1.2.0'
      var range = semver_helper.get_base_range(parsed);
      var max_patch = semver.maxSatisfying(versions, range);
      var max_stable_patch = stable.maxSatisfying(versions, range);
      var max = versions[0];
      var max_stable = stable.max(versions);

      if (
        // [1]
        // If the current minor(feature) doesn't exist, it is the max matched patch of the current minor
        !max_patch ||

        // Greater or equal to
        semver.gte(version, max_patch) ||

        // If the current version is greater or equal to the max stable patch,
        // we also let it go
        max_stable_patch && semver.gte(version, max_stable_patch)
      ) {
        // >>>> range
        tasks[range] = node_path.join(built_root, name, range);

        // if the package is not the latest patch,
        // it's impossible to be the latest of all versions
        if (
          // if there's no versions on the registry,
          // See 1
          !max ||

          // if current version is the greatest
          semver.gte(version, max) ||
          max_stable && semver.gte(version, max_stable)
        ) {
          // >>>> latest
          tasks.latest = node_path.join(built_root, name, 'latest');
        }
      }

      // {
      //     '1.2.3': '/xxx/~1.2.0',
      //     'latest': '/xxx/latest'
      // }
      callback(null, tasks);
    });

  } else {
    callback(null, tasks);
  }
};

build.build = function(options, callback) {
  var pkg = options.pkg;
  // distribution directory
  var dist = lang.object_member_by_namespaces(options.pkg, 'directories.dist');

  if (dist) {
    return this.dist(dist, options, callback);
  }

  var self = this;
  this.check_entry(options, function(err) {
    if (err) {
      return callback(err);
    }

    var tasks = options.tasks;
    async.each(Object.keys(tasks), function(target_version, done) {
      var to = tasks[target_version];
      async.parallel([
        function (done) {
          // Build JavaScript modules
          self.build_modules(target_version, to, options, done);
        },
        function (done) {
          self.copy(options.cwd, to, 'cortex-shrinkwrap.json', done);
        }
        function (done) {
          // Copy css
          self.copy_csses(sub_done);
        },
        function (done) {
          // Copy directories
          self.copy_directories(sub_done);
        }
      ], done);
    }, callback);
  });
};

build.dist = function (dist, options, callback) {
  dist = node_path.join(options.cwd, dist);
  // if `dist` dir exists, skip building and just copy it
  fs.exists(dist, function(exists) {
    if (!exists) {
      // if `cortex.directories.dist` is declared, the dir must be existed.
      return callback({
        code: 'EDISTNOTEXISTS',
        message: 'dist dir "' + dist + '" does not exist.',
        data: {
          dist: dist
        }
      });
    }

    this.logger.info('dist dir "' + dist + '" found, {{cyan skip}} building ...');
    this.copy_dist(dist, options, callback);
  }.bind(this));
};

// copy dist dir to the destination dirs
build.copy_dist = function(dist, options, callback) {
  var self = this;
  var tasks = options.tasks;
  async.each(Object.keys(tasks), function(task, done) {
    var to = tasks[task];
    self.logger.info('{{cyan copy}} "' + dist + '" to "' + to + '".');
    self.copy(dist, to, done);
  }, callback);
};

build.check_entry = function (options, callback) {
  var self = this;
  this.main_exists(options, function (exists) {
    if (exists) {
      var cwd = options.cwd;
      var pkg = options.pkg;
      // the module '<name>.js'
      var main_by_name = node_path.join(cwd, pkg.name) + '.js';
      var main_path = node_path.join(cwd, main_entry);

      fs.exists(main_by_name, function(exists) {
        if (exists && main_by_name !== main_path) {
          return callback({
            code: 'MAIN_CONFLICT',
            message: 'if "' + pkg.name + '.js" exists, it must be `package.main`, or you should rename it.',
            data: {
              name: pkg.name,
              main: pkg.main
            }
          });
        }
        self.get_entries(options, callback);
      });
    }

    if (!options.pkg.css) {
      return callback({
        code: 'NO_ENTRY_FOUND',
        message: 'Both `package.main` and `package.css` are not found. You need to define at least one.'
      });
    }
    callback(null);
  });
};

build.main_exists = function (options, callback) {
  var main_entry = options.pkg.main || 'index.js';

  // Standardize main entry
  // './index' -> './index.js'
  main_entry = this.ensure_ext(main_entry, 'js');
  // './index.js' -> 'index.js'
  main_entry = node_path.join('.', main_entry);
  var main_file = node_path.join(options.cwd, main_entry);

  fs.exists(main_file, function (exists) {
    if (exists) {
      options.main_entry = main_entry;
    }
    callback(exists);
  });
};

// Gets all entries, including
// - pkg.main
// - pkg.entries
build.get_entries = function(options, callback) {
  if (!options.pkg.entires) {
    options.entries = [options.main_entry];
    return callback(null);
  }

  expand(pkg.entries, {
    cwd: cwd
  }, function (err, entries) {
    if (err) {
      return callback(err);
    }
    options.entries = entries;
    // prevent duplication
    if (!~entries.indexOf(options.main_entry)) {
      entries.push(options.main_entry);
    }

    callback(null);
  });
};

build.ensure_ext = function(path, ext) {
  var regex = new RegExp('\\.' + ext + '$', 'i');
  if (!regex.test(path)) {
    path += '.' + ext;
  }
  return path;
};

// Builds JavaScript modules
build.build_modules = function(target_version, to, options, callback) {
  // Pure css package.
  if (!options.main_entry) {
    return callback(null);
  }

  var self = this;
  var cwd = options.cwd;
  async.each(options.entries, function (entry, done) {
    // Standardize entries
    // './index.js' -> 'index.js'
    entry = node_path.join('.', entry);
    var from = node_path.join(cwd, entry);

    builder(from, {
      cwd: cwd,
      targetVersion: target_version,
      pkg: options.pkg

    }, function (err, content) {
      if (err) {
        return done(err);
      }

      var file_to = entry === options.main_entry
        // main entry will
        ? options.pkg.name + '.js'
        : entry;

      var path_to = node_path.join(to, file_to);
      fse.mkdirs( node_path.dirname(path_to), function (err) {
        if (err) {
          return done(err);
        }

        self.logger.info('{{cyan write}} ' + path_to);
        fs.writeFile(path_to, content, done);
      });
    });

  }, callback);
};

build.copy_directories = function(to, options, callback) {
  var pkg = options.pkg;
  var directories = pkg.directories || {};

  var tasks = [
    // We only support `directories.src` for now.
    'src'
  ]
  .filter(function (dir) {
    return directories[dir];
  });

  async.each(tasks, function (name, done) {
    var dir = directories[dir];
    this.copy(options.cwd, to, dir, function (err) {
      if (err) {
        if (err.code === 'SRC_NOT_FOUND') {
          return done({
            code: 'DIR_NOT_FOUND',
            message: '`directories.' + name + '` is defined in cortex.json, but not found.',
            data: {
              name: name,
              dir
            }
          });
        } else {
          return done(err);
        }
      }
      done(null);
    }, true);

  }.bind(this), callback);
};

// Copy files from `from` to `to`
// @param {Boolean} strict
build.copy = function(from, to, item, callback, strict) {
  var self = this;
  if (from === to) {
    callback(null);
    return;
  }

  fs.exists(from, function (exists) {
    if (!exists) {
      // if strict and the source is not found, an error will throw.
      if (strict) {
        return callback({
          code: 'SRC_NOT_FOUND'
        });
      } else {
        return callback(null);
      }
    }

    self.logger.info('{{cyan copy}} ' + from + ' -> ' + to);
    fse.copy(from, to, callback);
  });
};
<|endoftext|>


<commit_before>'use strict';

const tryCatch = require('try-catch');
const squad = require('squad');
const currify = require('currify');
const find = require('../find');

const ifNotFound = currify((name, array) => {
    if (!find(name, array))
        throw Error(`runner with name "${name}" doesn\'t exist`);

    return array;
});

const clearDirs = currify((name, array) => {
    array.some((item) => {
        const is = name === item.name;

        if (is)
            item.directories = [];

        return is;
    });

    return array;
});

module.exports = (argv, runners, fn) => {
    const name = argv._[1];
    const clear = squad.apply(null, [
        clearDirs(name),
        ifNotFound(name),
    ]);

    let result;
    const error = tryCatch(() => {
        checkName(name);
        result = clear(runners);
    });

    fn(error, result);
};

function checkName(name) {
    if (!name)
        throw Error('name could not be empty');
}

<commit_msg>feature(clear) add if-not-found<commit_after>'use strict';

const tryCatch = require('try-catch');
const squad = require('squad');
const currify = require('currify');

const ifNotFound = require('../if-not-found');

const clearDirs = currify((name, array) => {
    array.some((item) => {
        const is = name === item.name;

        if (is)
            item.directories = [];

        return is;
    });

    return array;
});

module.exports = (argv, runners, fn) => {
    const name = argv._[1];
    const clear = squad.apply(null, [
        clearDirs(name),
        ifNotFound(name),
    ]);

    let result;
    const error = tryCatch(() => {
        checkName(name);
        result = clear(runners);
    });

    fn(error, result);
};

function checkName(name) {
    if (!name)
        throw Error('name could not be empty');
}

<|endoftext|>


<commit_before>/*jshint esversion:6, node:true*/
'use strict';

var winston = require('winston');
var config = require('winston/lib/winston/config');
var Message = require('../logger').Message;

/*
 * Winston:
 * We use the Console and daily rotate file transports
 * and if provided, we also use Honeybadger.
 */
var transports = [
    new (winston.transports.Console)({
        handleExceptions: true,
        prettyPrint: true,
        silent: false,
        level: 'silly',
        timestamp: 'hh:mm:ss',
        colorize: true,
        json: false,
        formatter: function(options) {
            var message = new Message()
                .setColorizer(config.colorize)
                .setTime(options.timestamp)
                .setLabel(options.label)
                .setLevel(options.level)
                .setFrom(options.from)
                .setMessage(options.message);

            if(options.meta){
                if(options.meta.loggerName) {
                    message.setLoggerName(options.meta.loggerName);
                }
            }

            return message.toString();
        }
    }),
    new (winston.transports.File)({
        filename: 'debug.log',
        // filename: './${app.name}-debug.log',
        name: 'file.debug',
        level: 'debug',
        maxsize: 1024000,
        maxFiles: 3,
        handleExceptions: true,
        json: false
    })
];

module.exports = {
    transports: transports,
    exceptionHandlers: [
        new winston.transports.File({
            filename: 'exceptions.log',
            maxsize: 1024000,
            maxFiles: 3,
            name: 'file.exceptions',
            json: false
        })
    ]
};

/*
 * This get's executed after we have solved
 * our dependencies and before we return the
 * final config object.
 */
 module.exports.afterSolver = function(config){
     /*
      * If we are in production, get rid of the
      */
     if(config.get('environment', 'production') === 'production'){
         var transports = [];
         config.logger.transports.map((tr)=> {
             if(tr.name === 'file.debug') return;
             transports.push(tr);
         });
         config.logger.transports =  transports;
     }

 };
<commit_msg>config: logger add setColors<commit_after>/*jshint esversion:6, node:true*/
'use strict';

var winston = require('winston');
var config = require('winston/lib/winston/config');
var Message = require('../logger').Message;

/*
 * Winston:
 * We use the Console and daily rotate file transports
 * and if provided, we also use Honeybadger.
 */
var transports = [
    new (winston.transports.Console)({
        handleExceptions: true,
        prettyPrint: true,
        silent: false,
        level: 'silly',
        timestamp: 'hh:mm:ss',
        colorize: true,
        json: false,
        formatter: function(options) {
            var message = new Message()
                .setColorizer(config.colorize)
                .setColors(config.allColors)
                .setTime(options.timestamp)
                .setLabel(options.label)
                .setLevel(options.level)
                .setFrom(options.from)
                .setMessage(options.message);

            if(options.meta){
                if(options.meta.loggerName) {
                    message.setLoggerName(options.meta.loggerName);
                }
            }

            return message.toString();
        }
    }),
    new (winston.transports.File)({
        filename: 'debug.log',
        // filename: './${app.name}-debug.log',
        name: 'file.debug',
        level: 'debug',
        maxsize: 1024000,
        maxFiles: 3,
        handleExceptions: true,
        json: false
    })
];

module.exports = {
    transports: transports,
    exceptionHandlers: [
        new winston.transports.File({
            filename: 'exceptions.log',
            maxsize: 1024000,
            maxFiles: 3,
            name: 'file.exceptions',
            json: false
        })
    ]
};

/*
 * This get's executed after we have solved
 * our dependencies and before we return the
 * final config object.
 */
 module.exports.afterSolver = function(config){
     /*
      * If we are in production, get rid of the
      */
     if(config.get('environment', 'production') === 'production'){
         var transports = [];
         config.logger.transports.map((tr)=> {
             if(tr.name === 'file.debug') return;
             transports.push(tr);
         });
         config.logger.transports =  transports;
     }

 };
<|endoftext|>