<commit_before>/*
 * Copyright 2013, All Rights Reserved.
 *
 * Code licensed under the BSD License:
 * https://github.com/node-gh/gh/blob/master/LICENSE.md
 *
 * @author Eduardo Lundgren <eduardolundgren@gmail.com>
 */

var async = require('async'),
    base = require('./base'),
    clc = require('cli-color'),
    exec = require('child_process').exec,
    spawn = require('child_process').spawn,
    logger = require('./logger');

exports.invoke = function(path, scope, opt_callback) {
    var after = exports.getHooksFromPath(path + '.after'),
        before = exports.getHooksFromPath(path + '.before'),
        afterOperations = [],
        beforeOperations = [],
        context;

    context = exports.createContext(scope);

    after.forEach(function(cmd) {
        afterOperations.push(exports.wrapCommand_(cmd, context));
    });

    before.forEach(function(cmd) {
        beforeOperations.push(exports.wrapCommand_(cmd, context));
    });

    async.series(beforeOperations, function() {
        opt_callback && opt_callback(function() {
            async.series(afterOperations);
        });
    });
};

exports.createContext = function(scope) {
    var config = base.getGlobalConfig();

    return {
        options: scope.options,
        signature: config.signature
    };
};

exports.getHooksArrayFromPath_ = function(path, opt_config) {
    var keys = path.split('.'),
        key = keys.shift(),
        hooks;

    opt_config = opt_config || base.getGlobalConfig();

    hooks = opt_config.hooks || {};

    while (hooks[key]) {
        hooks = hooks[key];
        key = keys.shift();
    }

    return Array.isArray(hooks) ? hooks : [];
};

exports.getHooksFromPath = function(path) {
    var hooks,
        plugins = base.getPlugins(),
        pluginHooks = [];

    // First, load all core hooks for the specified path.
    hooks = exports.getHooksArrayFromPath_(path);

    // Second, search all installed plugins and load the hooks for each into
    // core hooks array.
    plugins.forEach(function(plugin) {
        var config = base.getPluginConfig(plugin),
            pluginConfig;

        if (config.plugins && !base.isPluginIgnored(plugin)) {
            pluginConfig = config.plugins.jira;

            if (pluginConfig) {
                pluginHooks = pluginHooks.concat(
                    exports.getHooksArrayFromPath_(path, pluginConfig));
            }
        }
    });

    return hooks.concat(pluginHooks);
};

exports.exec_ = function(cmd, opt_args, opt_cwd, opt_log, callback) {
    var spawned;

    if (opt_args) {
        spawned = spawn(cmd, opt_args, {
            stdio: 'inherit'
        });

        spawned.on('close', callback);
    }
    else {
        exec(cmd, {
                cwd: opt_cwd,
                stdio: 'inherit'
            },
            function(error, stdout, stdin) {
                if (opt_log) {
                    logger.log(error, stdout, stdin);
                }

                if (error) {
                    logger.custom('[error]', clc.redBright, logger.getErrorMessage(error));
                }
                callback();
            });
    }
};

exports.wrapCommand_ = function(cmd, context) {
    return function(callback) {
        var args,
            raw = cmd,
            rawLog = raw,
            log = false;

        if (typeof raw === 'object') {
            args = cmd.args;
            log = cmd.log;
            raw = cmd.cmd;
        }

        raw = rawLog = logger.compileTemplate(raw, context);

        if (raw) {
            if (args) {
                // Compile template for arguments.
                args.forEach(function(arg, index) {
                    args[index] = logger.compileTemplate(arg, context);
                });

                rawLog += ' ' + args.join(' ');
            }

            logger.log('[hook]', rawLog.trim());
            exports.exec_(raw, args, process.cwd(), log, callback);
        }
        else {
            callback && callback();
        }
    };
};<commit_msg>Add ability to setup hooks from plugins - Fix #192<commit_after>/*
 * Copyright 2013, All Rights Reserved.
 *
 * Code licensed under the BSD License:
 * https://github.com/node-gh/gh/blob/master/LICENSE.md
 *
 * @author Eduardo Lundgren <eduardolundgren@gmail.com>
 */

var async = require('async'),
    base = require('./base'),
    clc = require('cli-color'),
    exec = require('child_process').exec,
    spawn = require('child_process').spawn,
    logger = require('./logger');

exports.invoke = function(path, scope, opt_callback) {
    var after = exports.getHooksFromPath(path + '.after'),
        before = exports.getHooksFromPath(path + '.before'),
        beforeOperations = [],
        afterOperations = [],
        context;

    context = exports.createContext(scope);

    beforeOperations.push(function(callback) {
        exports.setupPlugins_(context, 'setupBeforeHooks', callback);
    });

    before.forEach(function(cmd) {
        beforeOperations.push(exports.wrapCommand_(cmd, context));
    });

    afterOperations.push(function(callback) {
        exports.setupPlugins_(context, 'setupAfterHooks', callback);
    });

    after.forEach(function(cmd) {
        afterOperations.push(exports.wrapCommand_(cmd, context));
    });

    async.series(beforeOperations, function() {
        opt_callback && opt_callback(function() {
            async.series(afterOperations);
        });
    });
};

exports.createContext = function(scope) {
    var config = base.getGlobalConfig();

    return {
        options: scope.options,
        signature: config.signature
    };
};

exports.getHooksArrayFromPath_ = function(path, opt_config) {
    var keys = path.split('.'),
        key = keys.shift(),
        hooks;

    opt_config = opt_config || base.getGlobalConfig();

    hooks = opt_config.hooks || {};

    while (hooks[key]) {
        hooks = hooks[key];
        key = keys.shift();
    }

    return Array.isArray(hooks) ? hooks : [];
};

exports.getHooksFromPath = function(path) {
    var hooks,
        plugins = base.getPlugins(),
        pluginHooks = [];

    // First, load all core hooks for the specified path.
    hooks = exports.getHooksArrayFromPath_(path);

    // Second, search all installed plugins and load the hooks for each into
    // core hooks array.
    plugins.forEach(function(plugin) {
        var config = base.getPluginConfig(plugin),
            pluginConfig;

        if (config.plugins && !base.isPluginIgnored(plugin)) {
            pluginConfig = config.plugins.jira;

            if (pluginConfig) {
                pluginHooks = pluginHooks.concat(
                    exports.getHooksArrayFromPath_(path, pluginConfig));
            }
        }
    });

    return hooks.concat(pluginHooks);
};

exports.exec_ = function(cmd, opt_args, opt_cwd, opt_log, callback) {
    var spawned;

    if (opt_args) {
        spawned = spawn(cmd, opt_args, {
            stdio: 'inherit'
        });

        spawned.on('close', callback);
    }
    else {
        exec(cmd, {
                cwd: opt_cwd,
                stdio: 'inherit'
            },
            function(error, stdout, stdin) {
                if (opt_log) {
                    logger.log(error, stdout, stdin);
                }

                if (error) {
                    logger.custom('[error]', clc.redBright, logger.getErrorMessage(error));
                }
                callback();
            });
    }
};

exports.setupPlugins_ = function(context, setupFn, opt_callback) {
    var plugins = base.getPlugins(),
        operations = [];

    plugins.forEach(function(plugin) {
        operations.push(function(callback) {
            try {
                plugin = base.getPlugin(plugin);
                plugin[setupFn](context, callback);
            }
            catch (e) {
                callback();
            }
        });
    });

    async.series(operations, function() {
        opt_callback && opt_callback();
    });
};

exports.wrapCommand_ = function(cmd, context) {
    return function(callback) {
        var args,
            raw = cmd,
            rawLog = raw,
            log = false;

        if (typeof raw === 'object') {
            args = cmd.args;
            log = cmd.log;
            raw = cmd.cmd;
        }

        raw = rawLog = logger.compileTemplate(raw, context);

        if (raw) {
            if (args) {
                // Compile template for arguments.
                args.forEach(function(arg, index) {
                    args[index] = logger.compileTemplate(arg, context);
                });

                rawLog += ' ' + args.join(' ');
            }

            logger.log('[hook]', rawLog.trim());
            exports.exec_(raw, args, process.cwd(), log, callback);
        }
        else {
            callback && callback();
        }
    };
};<|endoftext|>


<commit_before>var util = require('./util');

/**
 * Represents an image
 * @param {Object} modem docker-modem
 * @param {String} name  Image's name
 */
var Image = function(modem, name) {
  this.modem = modem;
  this.name = name;
};

Image.prototype[require('util').inspect.custom] = function() { return this; };

/**
 * Inspect
 * @param  {Function} callback Callback, if specified Docker will be queried.
 * @return {Object}            Name only if callback isn't specified.
 */
Image.prototype.inspect = function(callback) {
  var self = this;

  var opts = {
    path: '/images/' + this.name + '/json',
    method: 'GET',
    statusCodes: {
      200: true,
      404: 'no such image',
      500: 'server error'
    }
  };

  if(callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(opts, function(err, data) {
        if (err) {
          return reject(err);
        }
        resolve(data);
      });
    });
  } else {
    this.modem.dial(opts, function(err, data) {
      if (err) return callback(err, data);
      callback(err, data);
    });
  }
};

/**
 * Distribution
 * @param  {Function} callback Callback, if specified Docker will be queried.
 * @return {Object}            Name only if callback isn't specified.
 */
Image.prototype.distribution = function(callback) {
  var self = this;

  var opts = {
    path: '/distribution/' + this.name + '/json',
    method: 'GET',
    statusCodes: {
      200: true,
      401: 'no such image',
      500: 'server error'
    }
  };

  if(callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(opts, function(err, data) {
        if (err) {
          return reject(err);
        }
        resolve(data);
      });
    });
  } else {
    this.modem.dial(opts, function(err, data) {
      if (err) return callback(err, data);
      callback(err, data);
    });
  }
};

/**
 * History
 * @param  {Function} callback Callback
 */
Image.prototype.history = function(callback) {
  var self = this;
  var opts = {
    path: '/images/' + this.name + '/history',
    method: 'GET',
    statusCodes: {
      200: true,
      404: 'no such image',
      500: 'server error'
    }
  };

  if(callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(opts, function(err, data) {
        if (err) {
          return reject(err);
        }
        resolve(data);
      });
    });
  } else {
    this.modem.dial(opts, function(err, data) {
      if (err) return callback(err, data);
      callback(err, data);
    });
  }
};

/**
 * Get
 * @param  {Function} callback Callback with data stream.
 */
Image.prototype.get = function(callback) {
  var self = this;
  var opts = {
    path: '/images/' + this.name + '/get',
    method: 'GET',
    isStream: true,
    statusCodes: {
      200: true,
      500: 'server error'
    }
  };

  if(callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(opts, function(err, data) {
        if (err) {
          return reject(err);
        }
        resolve(data);
      });
    });
  } else {
    this.modem.dial(opts, function(err, data) {
      if (err) return callback(err, data);
      callback(err, data);
    });
  }
};

/**
 * Push
 * @param  {Object}   opts     Push options, like 'registry' (optional)
 * @param  {Function} callback Callback with stream.
 * @param  {Object}   auth     Registry authentication
 */
Image.prototype.push = function(opts, callback, auth) {
  var self = this;
  var args = util.processArgs(opts, callback);
  var isStream = true;
  if (args.opts.stream === false) {
    isStream = false;
  }
  var optsf = {
    path: '/images/' + this.name + '/push?',
    method: 'POST',
    options: args.opts,
    authconfig: args.opts.authconfig || auth,
    isStream: isStream,
    statusCodes: {
      200: true,
      404: 'no such image',
      500: 'server error'
    }
  };

  delete optsf.options.authconfig;

  if(callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(optsf, function(err, data) {
        if (err) {
          return reject(err);
        }
        resolve(data);
      });
    });
  } else {
    this.modem.dial(optsf, function(err, data) {
      callback(err, data);
    });
  }
};

/**
 * Tag
 * @param  {Object}   opts     Tag options, like 'repo' (optional)
 * @param  {Function} callback Callback
 */
Image.prototype.tag = function(opts, callback) {
  var self = this;
  var optsf = {
    path: '/images/' + this.name + '/tag?',
    method: 'POST',
    options: opts,
    statusCodes: {
      200: true, // unofficial, but proxies may return it
      201: true,
      400: 'bad parameter',
      404: 'no such image',
      409: 'conflict',
      500: 'server error'
    }
  };

  if(callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(optsf, function(err, data) {
        if (err) {
          return reject(err);
        }
        self.output = data;
        resolve(self);
      });
    });
  } else {
    this.modem.dial(optsf, function(err, data) {
      callback(err, data);
    });
  }
};

/**
 * Removes the image
 * @param  {[Object]}   opts     Remove options (optional)
 * @param  {Function} callback Callback
 */
Image.prototype.remove = function(opts, callback) {
  var self = this;
  var args = util.processArgs(opts, callback);

  var optsf = {
    path: '/images/' + this.name + '?',
    method: 'DELETE',
    statusCodes: {
      200: true,
      404: 'no such image',
      409: 'conflict',
      500: 'server error'
    },
    options: args.opts
  };

  if(args.callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(optsf, function(err, data) {
        if (err) {
          return reject(err);
        }
        resolve(data);
      });
    });
  } else {
    this.modem.dial(optsf, function(err, data) {
      args.callback(err, data);
    });
  }
};

module.exports = Image;
<commit_msg>allow passing authconfig to Image.distribution<commit_after>var util = require('./util');

/**
 * Represents an image
 * @param {Object} modem docker-modem
 * @param {String} name  Image's name
 */
var Image = function(modem, name) {
  this.modem = modem;
  this.name = name;
};

Image.prototype[require('util').inspect.custom] = function() { return this; };

/**
 * Inspect
 * @param  {Function} callback Callback, if specified Docker will be queried.
 * @return {Object}            Name only if callback isn't specified.
 */
Image.prototype.inspect = function(callback) {
  var self = this;

  var opts = {
    path: '/images/' + this.name + '/json',
    method: 'GET',
    statusCodes: {
      200: true,
      404: 'no such image',
      500: 'server error'
    }
  };

  if(callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(opts, function(err, data) {
        if (err) {
          return reject(err);
        }
        resolve(data);
      });
    });
  } else {
    this.modem.dial(opts, function(err, data) {
      if (err) return callback(err, data);
      callback(err, data);
    });
  }
};

/**
 * Distribution
 * @param  {Function} callback Callback, if specified Docker will be queried.
 * @return {Object}            Name only if callback isn't specified.
 */
Image.prototype.distribution = function(auth, callback) {
  var self = this;
  if (!callback && typeof auth === 'function') {
    callback = auth;
    auth = undefined;
  }

  var opts = {
    path: '/distribution/' + this.name + '/json',
    method: 'GET',
    statusCodes: {
      200: true,
      401: 'no such image',
      500: 'server error'
    },
    authconfig: auth
  };

  if(callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(opts, function(err, data) {
        if (err) {
          return reject(err);
        }
        resolve(data);
      });
    });
  } else {
    this.modem.dial(opts, function(err, data) {
      if (err) return callback(err, data);
      callback(err, data);
    });
  }
};

/**
 * History
 * @param  {Function} callback Callback
 */
Image.prototype.history = function(callback) {
  var self = this;
  var opts = {
    path: '/images/' + this.name + '/history',
    method: 'GET',
    statusCodes: {
      200: true,
      404: 'no such image',
      500: 'server error'
    }
  };

  if(callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(opts, function(err, data) {
        if (err) {
          return reject(err);
        }
        resolve(data);
      });
    });
  } else {
    this.modem.dial(opts, function(err, data) {
      if (err) return callback(err, data);
      callback(err, data);
    });
  }
};

/**
 * Get
 * @param  {Function} callback Callback with data stream.
 */
Image.prototype.get = function(callback) {
  var self = this;
  var opts = {
    path: '/images/' + this.name + '/get',
    method: 'GET',
    isStream: true,
    statusCodes: {
      200: true,
      500: 'server error'
    }
  };

  if(callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(opts, function(err, data) {
        if (err) {
          return reject(err);
        }
        resolve(data);
      });
    });
  } else {
    this.modem.dial(opts, function(err, data) {
      if (err) return callback(err, data);
      callback(err, data);
    });
  }
};

/**
 * Push
 * @param  {Object}   opts     Push options, like 'registry' (optional)
 * @param  {Function} callback Callback with stream.
 * @param  {Object}   auth     Registry authentication
 */
Image.prototype.push = function(opts, callback, auth) {
  var self = this;
  var args = util.processArgs(opts, callback);
  var isStream = true;
  if (args.opts.stream === false) {
    isStream = false;
  }
  var optsf = {
    path: '/images/' + this.name + '/push?',
    method: 'POST',
    options: args.opts,
    authconfig: args.opts.authconfig || auth,
    isStream: isStream,
    statusCodes: {
      200: true,
      404: 'no such image',
      500: 'server error'
    }
  };

  delete optsf.options.authconfig;

  if(callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(optsf, function(err, data) {
        if (err) {
          return reject(err);
        }
        resolve(data);
      });
    });
  } else {
    this.modem.dial(optsf, function(err, data) {
      callback(err, data);
    });
  }
};

/**
 * Tag
 * @param  {Object}   opts     Tag options, like 'repo' (optional)
 * @param  {Function} callback Callback
 */
Image.prototype.tag = function(opts, callback) {
  var self = this;
  var optsf = {
    path: '/images/' + this.name + '/tag?',
    method: 'POST',
    options: opts,
    statusCodes: {
      200: true, // unofficial, but proxies may return it
      201: true,
      400: 'bad parameter',
      404: 'no such image',
      409: 'conflict',
      500: 'server error'
    }
  };

  if(callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(optsf, function(err, data) {
        if (err) {
          return reject(err);
        }
        self.output = data;
        resolve(self);
      });
    });
  } else {
    this.modem.dial(optsf, function(err, data) {
      callback(err, data);
    });
  }
};

/**
 * Removes the image
 * @param  {[Object]}   opts     Remove options (optional)
 * @param  {Function} callback Callback
 */
Image.prototype.remove = function(opts, callback) {
  var self = this;
  var args = util.processArgs(opts, callback);

  var optsf = {
    path: '/images/' + this.name + '?',
    method: 'DELETE',
    statusCodes: {
      200: true,
      404: 'no such image',
      409: 'conflict',
      500: 'server error'
    },
    options: args.opts
  };

  if(args.callback === undefined) {
    return new this.modem.Promise(function(resolve, reject) {
      self.modem.dial(optsf, function(err, data) {
        if (err) {
          return reject(err);
        }
        resolve(data);
      });
    });
  } else {
    this.modem.dial(optsf, function(err, data) {
      args.callback(err, data);
    });
  }
};

module.exports = Image;
<|endoftext|>


<commit_before>// Dependencies
var Promise = require("bluebird")
  , Fs = Promise.promisifyAll(require("fs"))
  , Ul = require("ul")
  , JxUtils = require("jxutils")
  , NpmKeyword = require("npm-keyword")
  , PackageJson = require("package-json")
  , FsPlus = require("fs-plus")
  , Async = require("async")
  , ReadJson = FsPlus.readJSON = require("read-json")
  , WriteJson = FsPlus.writeJSON = require("write-json")
  , Gry = require("gry")
  , GitUrlParse = require("giturlparse")
  , Tmp = require("tmp")
  , OArgv = require("oargv")
  , ExecLimiter = require("exec-limiter")
  ;

// Create the exec limiter
var execLimit = new ExecLimiter(10);

// Constants
const DEFAULT_PACKAGE = {
        components: {
            scripts: []
          , styles: []
          , markup: []
        }
      , flow: []
      , clientFlow: []
      , keywords: []
    }
  , DOT_SERVICE = {
        client: {
            methods: {}
          , emits: {}
        }
    }
  , JXENGINE_KEY = "jxengine"
  , ENGINE_APPS = process.env.ENGINE_APPS
  ;

function EngineTools() {}
EngineTools.Module = require("./module");

/**
 * generateModuleService
 * Generates content for .service.json file.
 *
 * @name generateModuleService
 * @function
 * @param {String} path The path to the package.json file.
 * @param {Function} callback A function called with error and .serivce.json file content.
 * @return {Promise} A promise used to run the function steps.
 */
EngineTools.prototype.generateModuleService = function (path, callback) {

    if (typeof path === "function") {
        callback = path;
        path = process.cwd() + "/package.json";
    }

    Fs.readFileAsync(path)
        .then(JSON.parse)
        .then(function (package) {
            package = Ul.merge(package, DEFAULT_PACKAGE);

            var dotService = Ul.clone(DOT_SERVICE)
              , mainFile = package.components.scripts[0]
              , mod = require(path.split("/").slice(0, -1).join("/") + "/" + mainFile)
              ;

            try {
                mod.init.call(mod);
            } catch (e) {}

            var methods = Object.keys(JxUtils.flattenObject(mod));
            methods.forEach(function (c) {
                if (c === "init" || !JxUtils.findFunction(mod, c)) { return; }
                dotService.client.methods[c] = {};
            });

            return dotService;
        })
        .then(function (dotService) {
            callback(null, dotService);
        })
        .catch(function (err) {
            callback(err);
        })
        ;
};

EngineTools.prototype.generateSymlinks = function (path, callback) {
    // TODO
    throw new Error("Not yet implemented.");
};

/**
 * packPath
 * Returns the full path to the package.json located into `path`.
 *
 * @name packPath
 * @function
 * @param {String} path The path to the directory containing the package.json file.
 * @return {String} The path to the package.json.
 */
EngineTools.prototype.packPath = function (path) {
    return FsPlus.normalize(path + "/package.json");
};

/**
 * projectPackPath
 * Returns the full path to the package.json located in the project.
 *
 * @name projectPackPath
 * @function
 * @param {String} project The project name.
 * @return {String} The path to the project package.json file.
 */
EngineTools.prototype.projectPackPath = function (project) {
    return this.packPath(this.getProjectPath(project));
};

/**
 * getProjectPath
 * Returns the full path to the project directory.
 *
 * @name getProjectPath
 * @function
 * @param {String} project The project name.
 * @return {String} The full path to the project directory.
 */
EngineTools.prototype.getProjectPath = function (project) {
    return FsPlus.normalize(ENGINE_APPS + "/" + project);
};

/**
 * projectExistsSync
 * Checks if the project exists or not.
 *
 * @name projectExistsSync
 * @function
 * @param {String} project The project name.
 * @return {Boolean} `true` if the project exists, `false` otherwise.
 */
EngineTools.prototype.projectExistsSync = function (project) {
    return FsPlus.existsSync(this.getProjectPath(project));
};

/**
 * projectPack
 * Fetches the project package.json object.
 *
 * @name projectPack
 * @function
 * @param {String} project The project name.
 * @param {Function} callback The callback function.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.projectPack = function (project, callback) {
    FsPlus.readJSON(this.projectPackPath(project), callback);
    return this;
};

/**
 * getNpmModules
 * Fetches the Engine modules from NPM.
 *
 * @name getNpmModules
 * @function
 * @param {Function} callback The callback function.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.getNpmModules = function (callback) {
    NpmKeyword(JXENGINE_KEY, function (err, packages) {
        if (err) { return callback(err); }
        packages = packages.map(function (c) {
            return new EngineTools.Module(c);
        });
        callback(null, packages);
    });
    return this;
};

/**
 * isEngineModule
 * Checks if the input is an Engine module.
 *
 * Usage
 *
 * ```js
 * // Path to the module directory
 * EngineTools.isEngineModule("path/to/node_modules/foo", function (isEngineModule, pack) {
 *    // do something
 * });
 *
 * // Path to the package.json
 * EngineTools.isEngineModule("path/to/node_modules/foo/package.json", function (isEngineModule, pack) {
 *    // do something
 * });
 *
 * // Some object
 * EngineTools.isEngineModule({
 *    keywords: ["jxengine"]
 * }, function (isEngineModule, pack) {
 *    // do something
 * });
 * ```
 *
 * @name isEngineModule
 * @function
 * @param {String|Object} input A string representing the path to a directory containing the package.json file or even to the package.json file or the package object itself.
 * @param {Function} callback The callback function called with two parameters: a boolean value (`true` if the provided input is an Engine module) and the package object.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.isEngineModule = function (input, callback) {

    var self = this;

    if (typeof input === "string") {
        if (!/package\.json$/.test(input)) {
            input += "/package.json";
        }
        FsPlus.readJSON(input, function (err, package) {
            self.isEngineModule(package, callback);
        });
        return self;
    }

    input = Ul.merge(input, DEFAULT_PACKAGE);
    callback(input.keywords.indexOf(JXENGINE_KEY) !== -1, input);

    return self;
};

/**
 * appModules
 * Fetches the installed Engine modules.
 *
 * @name appModules
 * @function
 * @param {String} path The path to the project.
 * @param {Function} callback The callback function.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.appModules = function (path, callback) {
    var self = this;
    path = FsPlus.normalize(path);
    FsPlus.list(path + "/node_modules", [""], function (err, node_modules) {
        if (err) { return callback(err); }
        var engineModules = [];
        Async.parallel(node_modules.map(function (c) {
            return function (opt_callback) {
                self.isEngineModule(c, function (is, pack) {
                    if (!is) { return opt_callback(null, null); }
                    engineModules.push(new EngineTools.Module(pack));
                    opt_callback(null);
                });
            }
        }), function (err) {
            callback(err, engineModules);
        });
    });
    return self;
};

/**
 * availableModules
 * Fetches the available modules for a project.
 *
 * @name availableModules
 * @function
 * @param {String} app The project name.
 * @param {Function} callback The callback function.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.availableModules = function (app, callback) {

    var self = this
      , appPath = self.getProjectPath(app)
      ;

    if (!self.projectExistsSync(app)) {
        return callback(new Error("This project doesn't exist."));
    }

    var res = [];
    Async.parallel([
        self.getNpmModules
      , self.appModules.bind(self, appPath)
    ], function (err, data) {
        if (err) { return callback(err); }
        var uniq = {};
        data[0].concat(data[1]).forEach(function (c) {
            uniq[c.name] = c;
        });
        Object.keys(uniq).forEach(function (c) {
            res.push(uniq[c]);
        });
        callback(err, res);
    });

    return self;
};

/**
 * getModuleInfo
 * Gets information about a provided module.
 *
 * @name getModuleInfo
 * @function
 * @param {String} module The module to get info about (can be a npm module name or a git url).
 * @param {Function} callback The callback function.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.getModuleInfo = function (module, callback) {
    var self = this
      , parsed = GitUrlParse(module)
      , info = {
            fromNpm: false
          , git_url: null
          , version: null
          , name: ""
        }
      ;

    // Suppose it's a npm package
    if (parsed.protocol === "file") {
        PackageJson(module, "latest", function (err, res) {
            if (err) { return callback(err); }
            var inf = res[Object.keys(res)[0]];
            info.fromNpm = true;
            info.version = inf.version;
            info.name = inf.name;
            callback(null, info);
        });
    }

    // Git url
    else {
        var repo = new Gry(Tmp.dirSync().name);
        repo.exec(OArgv({
            _: [module, "."]
        }, "clone"), function (err) {
            if (err) { return callback(err); }
            FsPlus.readJSON(self.packPath(repo.cwd), function (err, pack) {
                if (err) { return callback(err); }
                info.version = pack.version;
                info.name = pack.name;
                info.git_url = parsed.source === "github.com"
                            ? parsed.owner + "/" + parsed.name + ".git"
                            : parsed.toString("ssh")
                            ;
                callback(null, info);
            });
        });
    }

    return self;
};

/**
 * installModule
 * Installs a provided module in a provided project.
 *
 * @name installModule
 * @function
 * @param {} project The project name.
 * @param {String} module The module to install (can be a npm module name or a git url).
 * @param {Function} callback The callback function.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.installModule = function (project, module, callback) {
    var self = this;
    self.projectPack(project, function (err, pack) {
        if (err) { return callback(err); }
        pack = Ul.merge(pack, {
            dependencies: {}
        });

        self.getModuleInfo(module, function (err, modInfo) {
            if (err) { return callback(err); }

            function add(clb) {
                if (pack.dependencies[modInfo.name]) {
                    return clb();
                }
                pack.dependencies[modInfo.name] = modInfo.fromNpm ? modInfo.version : modInfo.git_url;
                FsPlus.writeJSON(self.projectPackPath(project), pack, clb);
            }

            function install(clb) {
                var mod = modInfo.fromNpm ? [modInfo.name, modInfo.version].join("@") : pack.dependencies[modInfo.name];
                if (FsPlus.isDirectorySync(self.getProjectPath(project) + "/node_modules/" + modInfo.name)) {
                    return clb(null, pack);
                }
                execLimit.add(OArgv({
                    _: ["i", mod]
                }, "npm"), { cwd: self.getProjectPath(project) }, function (err) {
                    if (err) { return callback(err); }
                    callback(null, pack);
                });
            }

            add(function (err) {
                if (err) { return callback(err); }
                install(callback);
            });
        });
    });
    return self;
};

module.exports = new EngineTools();
<commit_msg>Take the module information correctly. Fixed #12.<commit_after>// Dependencies
var Promise = require("bluebird")
  , Fs = Promise.promisifyAll(require("fs"))
  , Ul = require("ul")
  , JxUtils = require("jxutils")
  , NpmKeyword = require("npm-keyword")
  , PackageJson = require("package-json")
  , FsPlus = require("fs-plus")
  , Async = require("async")
  , ReadJson = FsPlus.readJSON = require("read-json")
  , WriteJson = FsPlus.writeJSON = require("write-json")
  , Gry = require("gry")
  , GitUrlParse = require("giturlparse")
  , Tmp = require("tmp")
  , OArgv = require("oargv")
  , ExecLimiter = require("exec-limiter")
  ;

// Create the exec limiter
var execLimit = new ExecLimiter(10);

// Constants
const DEFAULT_PACKAGE = {
        components: {
            scripts: []
          , styles: []
          , markup: []
        }
      , flow: []
      , clientFlow: []
      , keywords: []
    }
  , DOT_SERVICE = {
        client: {
            methods: {}
          , emits: {}
        }
    }
  , JXENGINE_KEY = "jxengine"
  , ENGINE_APPS = process.env.ENGINE_APPS
  ;

function EngineTools() {}
EngineTools.Module = require("./module");

/**
 * generateModuleService
 * Generates content for .service.json file.
 *
 * @name generateModuleService
 * @function
 * @param {String} path The path to the package.json file.
 * @param {Function} callback A function called with error and .serivce.json file content.
 * @return {Promise} A promise used to run the function steps.
 */
EngineTools.prototype.generateModuleService = function (path, callback) {

    if (typeof path === "function") {
        callback = path;
        path = process.cwd() + "/package.json";
    }

    Fs.readFileAsync(path)
        .then(JSON.parse)
        .then(function (package) {
            package = Ul.merge(package, DEFAULT_PACKAGE);

            var dotService = Ul.clone(DOT_SERVICE)
              , mainFile = package.components.scripts[0]
              , mod = require(path.split("/").slice(0, -1).join("/") + "/" + mainFile)
              ;

            try {
                mod.init.call(mod);
            } catch (e) {}

            var methods = Object.keys(JxUtils.flattenObject(mod));
            methods.forEach(function (c) {
                if (c === "init") { return; }
                dotService.client.methods[c] = {};
            });

            return dotService;
        })
        .then(function (dotService) {
            callback(null, dotService);
        })
        .catch(function (err) {
            callback(err);
        })
        ;
};

EngineTools.prototype.generateSymlinks = function (path, callback) {
    // TODO
    throw new Error("Not yet implemented.");
};

/**
 * packPath
 * Returns the full path to the package.json located into `path`.
 *
 * @name packPath
 * @function
 * @param {String} path The path to the directory containing the package.json file.
 * @return {String} The path to the package.json.
 */
EngineTools.prototype.packPath = function (path) {
    return FsPlus.normalize(path + "/package.json");
};

/**
 * projectPackPath
 * Returns the full path to the package.json located in the project.
 *
 * @name projectPackPath
 * @function
 * @param {String} project The project name.
 * @return {String} The path to the project package.json file.
 */
EngineTools.prototype.projectPackPath = function (project) {
    return this.packPath(this.getProjectPath(project));
};

/**
 * getProjectPath
 * Returns the full path to the project directory.
 *
 * @name getProjectPath
 * @function
 * @param {String} project The project name.
 * @return {String} The full path to the project directory.
 */
EngineTools.prototype.getProjectPath = function (project) {
    return FsPlus.normalize(ENGINE_APPS + "/" + project);
};

/**
 * projectExistsSync
 * Checks if the project exists or not.
 *
 * @name projectExistsSync
 * @function
 * @param {String} project The project name.
 * @return {Boolean} `true` if the project exists, `false` otherwise.
 */
EngineTools.prototype.projectExistsSync = function (project) {
    return FsPlus.existsSync(this.getProjectPath(project));
};

/**
 * projectPack
 * Fetches the project package.json object.
 *
 * @name projectPack
 * @function
 * @param {String} project The project name.
 * @param {Function} callback The callback function.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.projectPack = function (project, callback) {
    FsPlus.readJSON(this.projectPackPath(project), callback);
    return this;
};

/**
 * getNpmModules
 * Fetches the Engine modules from NPM.
 *
 * @name getNpmModules
 * @function
 * @param {Function} callback The callback function.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.getNpmModules = function (callback) {
    NpmKeyword(JXENGINE_KEY, function (err, packages) {
        if (err) { return callback(err); }
        packages = packages.map(function (c) {
            return new EngineTools.Module(c);
        });
        callback(null, packages);
    });
    return this;
};

/**
 * isEngineModule
 * Checks if the input is an Engine module.
 *
 * Usage
 *
 * ```js
 * // Path to the module directory
 * EngineTools.isEngineModule("path/to/node_modules/foo", function (isEngineModule, pack) {
 *    // do something
 * });
 *
 * // Path to the package.json
 * EngineTools.isEngineModule("path/to/node_modules/foo/package.json", function (isEngineModule, pack) {
 *    // do something
 * });
 *
 * // Some object
 * EngineTools.isEngineModule({
 *    keywords: ["jxengine"]
 * }, function (isEngineModule, pack) {
 *    // do something
 * });
 * ```
 *
 * @name isEngineModule
 * @function
 * @param {String|Object} input A string representing the path to a directory containing the package.json file or even to the package.json file or the package object itself.
 * @param {Function} callback The callback function called with two parameters: a boolean value (`true` if the provided input is an Engine module) and the package object.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.isEngineModule = function (input, callback) {

    var self = this;

    if (typeof input === "string") {
        if (!/package\.json$/.test(input)) {
            input += "/package.json";
        }
        FsPlus.readJSON(input, function (err, package) {
            self.isEngineModule(package, callback);
        });
        return self;
    }

    input = Ul.merge(input, DEFAULT_PACKAGE);
    callback(input.keywords.indexOf(JXENGINE_KEY) !== -1, input);

    return self;
};

/**
 * appModules
 * Fetches the installed Engine modules.
 *
 * @name appModules
 * @function
 * @param {String} path The path to the project.
 * @param {Function} callback The callback function.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.appModules = function (path, callback) {
    var self = this;
    path = FsPlus.normalize(path);
    FsPlus.list(path + "/node_modules", [""], function (err, node_modules) {
        if (err) { return callback(err); }
        var engineModules = [];
        Async.parallel(node_modules.map(function (c) {
            return function (opt_callback) {
                self.isEngineModule(c, function (is, pack) {
                    if (!is) { return opt_callback(null, null); }
                    engineModules.push(new EngineTools.Module(pack));
                    opt_callback(null);
                });
            }
        }), function (err) {
            callback(err, engineModules);
        });
    });
    return self;
};

/**
 * availableModules
 * Fetches the available modules for a project.
 *
 * @name availableModules
 * @function
 * @param {String} app The project name.
 * @param {Function} callback The callback function.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.availableModules = function (app, callback) {

    var self = this
      , appPath = self.getProjectPath(app)
      ;

    if (!self.projectExistsSync(app)) {
        return callback(new Error("This project doesn't exist."));
    }

    var res = [];
    Async.parallel([
        self.getNpmModules
      , self.appModules.bind(self, appPath)
    ], function (err, data) {
        if (err) { return callback(err); }
        var uniq = {};
        data[0].concat(data[1]).forEach(function (c) {
            uniq[c.name] = c;
        });
        Object.keys(uniq).forEach(function (c) {
            res.push(uniq[c]);
        });
        callback(err, res);
    });

    return self;
};

/**
 * getModuleInfo
 * Gets information about a provided module.
 *
 * @name getModuleInfo
 * @function
 * @param {String} module The module to get info about (can be a npm module name or a git url).
 * @param {Function} callback The callback function.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.getModuleInfo = function (module, callback) {
    var self = this
      , parsed = GitUrlParse(module)
      , info = {
            fromNpm: false
          , git_url: null
          , version: null
          , name: ""
        }
      ;

    // Suppose it's a npm package
    if (parsed.protocol === "file") {
        PackageJson(module, "latest", function (err, res) {
            if (err) { return callback(err); }
            info.fromNpm = true;
            info.version = res.version;
            info.name = res.name;
            callback(null, info);
        });
    }

    // Git url
    else {
        var repo = new Gry(Tmp.dirSync().name);
        repo.exec(OArgv({
            _: [module, "."]
        }, "clone"), function (err) {
            if (err) { return callback(err); }
            FsPlus.readJSON(self.packPath(repo.cwd), function (err, pack) {
                if (err) { return callback(err); }
                info.version = pack.version;
                info.name = pack.name;
                info.git_url = parsed.source === "github.com"
                            ? parsed.owner + "/" + parsed.name + ".git"
                            : parsed.toString("ssh")
                            ;
                callback(null, info);
            });
        });
    }

    return self;
};

/**
 * installModule
 * Installs a provided module in a provided project.
 *
 * @name installModule
 * @function
 * @param {} project The project name.
 * @param {String} module The module to install (can be a npm module name or a git url).
 * @param {Function} callback The callback function.
 * @return {EngineTools} The `EngineTools` instance.
 */
EngineTools.prototype.installModule = function (project, module, callback) {
    var self = this;
    self.projectPack(project, function (err, pack) {
        if (err) { return callback(err); }
        pack = Ul.merge(pack, {
            dependencies: {}
        });

        self.getModuleInfo(module, function (err, modInfo) {
            if (err) { return callback(err); }

            function add(clb) {
                if (pack.dependencies[modInfo.name]) {
                    return clb();
                }
                pack.dependencies[modInfo.name] = modInfo.fromNpm ? modInfo.version : modInfo.git_url;
                FsPlus.writeJSON(self.projectPackPath(project), pack, clb);
            }

            function install(clb) {
                var mod = modInfo.fromNpm ? [modInfo.name, modInfo.version].join("@") : pack.dependencies[modInfo.name];
                if (FsPlus.isDirectorySync(self.getProjectPath(project) + "/node_modules/" + modInfo.name)) {
                    return clb(null, pack);
                }
                execLimit.add(OArgv({
                    _: ["i", mod]
                }, "npm"), { cwd: self.getProjectPath(project) }, function (err) {
                    if (err) { return callback(err); }
                    callback(null, pack);
                });
            }

            add(function (err) {
                if (err) { return callback(err); }
                install(callback);
            });
        });
    });
    return self;
};

module.exports = new EngineTools();
<|endoftext|>


<commit_before>(function() {
  var path = require('path'),
    crypto = require('crypto'),
    _s = require('underscore.string'),
    Zip = require('node-zip');

  var MESSAGE_PREFIX = '__MSG_',
    MESSAGE_SUFFIX = '__',
    DEFAULT_LOCALE = 'en';

  function parseCRXHeader(data) {
    var header = {};
    header.magic = data.substr(0, 4);
    header.version = readUint32FromString(data, 4);
    header.publicKeyLength = readUint32FromString(data, 8);
    header.signatureLength = readUint32FromString(data, 12);
    header.publicKey = data.substr(16, header.publicKeyLength);
    header.signature = data.substr(16 + header.publicKeyLength, header.signatureLength);
    header.length = 16 + header.publicKeyLength + header.signatureLength;
    return header;
  }

  function generateExtensionID(publicKey) {
    var sha256 = crypto.createHash('sha256');
    sha256.update(publicKey, 'binary');
    var sha = sha256.digest('hex').substr(0, 32);
    var charCode = 'a'.charCodeAt(0);
    var remapped = '';
    for (var i=0; i<sha.length; i++) {
      remapped += String.fromCharCode(parseInt(sha[i], 16) + charCode)[0];
    }
    return remapped;
  }

  function readUint32FromString(str, offset) {
    return str.charCodeAt(offset) +
      str.charCodeAt(offset+1) * 256 +
      str.charCodeAt(offset+2) * (256+256) +
      str.charCodeAt(offset+3) * (256*256*256);
  }

  function CRXFile(data) {
    this._data = data;
    this._header = null;
    this._manifest = null;
    this._extensionID = null;
    this._locales = {};
  }

  CRXFile.prototype = {
    get header() {
      return this._header;
    },

    get manifest() {
      return this._manifest;
    },

    get extensionID() {
      return this._extensionID;
    },

    localizeMessage: function(msg, locale) {
      locale = locale || DEFAULT_LOCALE;
      if (_s.startsWith(msg, MESSAGE_PREFIX) && _s.endsWith(msg, MESSAGE_SUFFIX)) {
        var key = msg.substring(MESSAGE_PREFIX.length, msg.length-MESSAGE_SUFFIX.length);
        return this._locales[locale][key].message;
      }
      else {
        return msg;
      }
    },

    unpack: function(fileCallback, doneCallback) {
      this._header = parseCRXHeader(this._data);
      this._extensionID = generateExtensionID(this._header.publicKey);
      var zip = new Zip(this._data.substr(this._header.length));
      var manifest;

      for (var index in zip.files) {
        var zipEntry = zip.files[index];
        var filename = zipEntry.name;
        fileCallback(filename, zipEntry.asBinary());
        if ('manifest.json' === filename) {
          this._manifest = JSON.parse(zipEntry.asText());
        }
        else if (_s.endsWith(filename, 'messages.json')) {
          var segments = filename.split(path.sep);
          if (3 === segments.length &&
            '_locales' === segments[0] &&
            'messages.json' === segments[2]) {
            this._locales[segments[1]] = JSON.parse(zipEntry.asText());
          }
        }
      }

      doneCallback();
    },

    getBestIcon: function(preferredSize) {
      var icons = this._manifest.icons;
      if (icons) {
        var bestSize = 0;
        // Simpleminded algorithm to get best icon size.
        // Optimal is exact match, otherwise use the largest one available.
        for (var sizeStr in icons) {
          var size = parseInt(sizeStr, 10);
          if (preferredSize === size) {
            // Optimal size
            bestSize = size;
            break;
          }
          else if (size > bestSize) {
            bestSize = size;
          }
        }
        if (bestSize) {
          return icons[bestSize];
        }
      }
    }
  };

  exports.CRXFile = CRXFile;
}).call(this);
<commit_msg>Update crxfile.js<commit_after>(function() {
  var path = require('path'),
    crypto = require('crypto'),
    _s = require('underscore.string'),
    Zip = require('node-zip');

  var MESSAGE_PREFIX = '__MSG_',
    MESSAGE_SUFFIX = '__',
    DEFAULT_LOCALE = 'en';

  function parseCRXHeader(data) {
    var header = {};
    header.magic = data.substr(0, 4);
    header.version = readUint32FromString(data, 4);
    header.publicKeyLength = readUint32FromString(data, 8);
    header.signatureLength = readUint32FromString(data, 12);
    header.publicKey = data.substr(16, header.publicKeyLength);
    header.signature = data.substr(16 + header.publicKeyLength, header.signatureLength);
    header.length = 16 + header.publicKeyLength + header.signatureLength;
    return header;
  }

  function generateExtensionID(publicKey) {
    var sha256 = crypto.createHash('sha256');
    sha256.update(publicKey, 'binary');
    var sha = sha256.digest('hex').substr(0, 32);
    var charCode = 'a'.charCodeAt(0);
    var remapped = '';
    for (var i=0; i<sha.length; i++) {
      remapped += String.fromCharCode(parseInt(sha[i], 16) + charCode)[0];
    }
    return remapped;
  }

  function readUint32FromString(str, offset) {
    return str.charCodeAt(offset) +
      str.charCodeAt(offset+1) * 256 +
      str.charCodeAt(offset+2) * (256+256) +
      str.charCodeAt(offset+3) * (256*256*256);
  }

  function CRXFile(data) {
    this._data = data;
    this._header = null;
    this._manifest = null;
    this._extensionID = null;
    this._locales = {};
  }

  CRXFile.prototype = {
    get header() {
      return this._header;
    },

    get manifest() {
      return this._manifest;
    },

    get extensionID() {
      return this._extensionID;
    },

    localizeMessage: function(msg, locale) {
      locale = locale || DEFAULT_LOCALE;
      if (_s.startsWith(msg, MESSAGE_PREFIX) && _s.endsWith(msg, MESSAGE_SUFFIX)) {
        var key = msg.substring(MESSAGE_PREFIX.length, msg.length-MESSAGE_SUFFIX.length);
        return this._locales[locale][key].message;
      }
      else {
        return msg;
      }
    },

    unpack: function(fileCallback, doneCallback) {
      this._header = parseCRXHeader(this._data);
      this._extensionID = generateExtensionID(this._header.publicKey);
      var zip = new Zip(this._data.substr(this._header.length));
      var manifest;

      for (var index in zip.files) {
        var zipEntry = zip.files[index];
        var filename = zipEntry.name;
        fileCallback(filename, zipEntry.asBinary());
        if ('manifest.json' === filename) {
          this._manifest = JSON.parse(zipEntry.asText());
        }
        else if (_s.endsWith(filename, 'messages.json')) {
          var segments = filename.split('/');
          if (3 === segments.length &&
            '_locales' === segments[0] &&
            'messages.json' === segments[2]) {
            this._locales[segments[1]] = JSON.parse(zipEntry.asText());
          }
        }
      }

      doneCallback();
    },

    getBestIcon: function(preferredSize) {
      var icons = this._manifest.icons;
      if (icons) {
        var bestSize = 0;
        // Simpleminded algorithm to get best icon size.
        // Optimal is exact match, otherwise use the largest one available.
        for (var sizeStr in icons) {
          var size = parseInt(sizeStr, 10);
          if (preferredSize === size) {
            // Optimal size
            bestSize = size;
            break;
          }
          else if (size > bestSize) {
            bestSize = size;
          }
        }
        if (bestSize) {
          return icons[bestSize];
        }
      }
    }
  };

  exports.CRXFile = CRXFile;
}).call(this);
<|endoftext|>


<commit_before>var express = require('express');
var path = require('path');
var fs = require('fs');
var http = require('http');
var EventEmitter = require('events').EventEmitter;
var util = require('./util');
var logger = require('./util/logger');
var rules = require('./rules');
var setupHttps = require('./https').setup;
var httpsUtil = require('./https/util');
var rulesUtil = require('./rules/util');
var initDataServer = require('./util/data-server');
var initLogServer = require('./util/log-server');
var pluginMgr = require('./plugins');
var config = require('./config');
var loadService = require('./service');
var initSocketMgr = require('./socket-mgr');
var tunnelProxy = require('./tunnel');
var upgradeProxy = require('./upgrade');

function proxy(callback) {
  var app = express();
  var server = http.createServer();
  var proxyEvents = new EventEmitter();
  var middlewares = ['./init', '../biz']
    .concat(require('./inspectors'))
    .concat(config.middlewares)
    .concat(require('./handlers'));
  server.timeout = server.keepAliveTimeout = config.idleTimeout;
  proxyEvents.config = config;
  app.logger = logger;
  middlewares.forEach(function(mw) {
    mw && app.use((typeof mw == 'string' ? require(mw) : mw).bind(proxyEvents));
  });
  server.on('clientError', function(err, socket) {
    if (!socket.writable) {
      return socket.destroy(err);
    }
    var stack = util.getErrorStack(err);
    socket.end('HTTP/1.1 400 Bad Request\r\n\r\n' + stack);
  });
  initSocketMgr(proxyEvents);
  setupHttps(proxyEvents);
  exportInterfaces(proxyEvents);
  tunnelProxy(server, proxyEvents);
  upgradeProxy(server);
  initDataServer(proxyEvents);
  initLogServer(proxyEvents);
  var properties = rulesUtil.properties;
  if (config.disableAllRules) {
    properties.set('disabledAllRules', true);
  } else if (config.disableAllRules === false) {
    properties.set('disabledAllRules', false);
  }
  if (config.disableAllPlugins) {
    properties.set('disabledAllPlugins', true);
  } else if (config.disableAllPlugins === false) {
    properties.set('disabledAllPlugins', false);
  }
  if (config.allowMultipleChoice) {
    properties.set('allowMultipleChoice', true);
  } else if (config.allowMultipleChoice === false) {
    properties.set('allowMultipleChoice', false);
  }
  rulesUtil.addValues(config.values, config.replaceExistValue);
  rulesUtil.addRules(config.rules, config.replaceExistRule);
  config.debug && rules.disableDnsCache();
  var count = 2;
  var execCallback = function() {
    if (--count === 0 && typeof callback === 'function') {
      callback.call(server, proxyEvents);
    }
  };
  if (config.host) {
    server.listen(config.port, config.host, execCallback);
  } else {
    server.listen(config.port, execCallback);
  }
  require('../biz/init')(proxyEvents, function(){
    server.on('request', app);
    execCallback();
  });
  return proxyEvents;
}

function exportInterfaces(obj) {
  obj.rules = rules;
  obj.util = util;
  obj.rulesUtil = rulesUtil;
  obj.httpsUtil = httpsUtil;
  obj.pluginMgr = pluginMgr;
  obj.logger = logger;
  obj.loadService = loadService;
  obj.setAuth = config.setAuth;
  obj.socketMgr = initSocketMgr;
  return obj;
}

process.on('uncaughtException', function(err){
  var stack = util.getErrorStack(err);
  var now = new Date().toLocaleString();
  var sep = '\r\n======================uncaughtException(' + now + ')======================\r\n';
  stack = sep + stack + '\r\n' + sep;
  fs.writeFileSync(path.join(process.cwd(), config.name + '.log'), '\r\n' + stack + '\r\n', {flag: 'a'});
  /*eslint no-console: "off"*/
  console.error(stack);
  process.exit(1);
});

module.exports = exportInterfaces(proxy);
<commit_msg>chore: refine log<commit_after>var express = require('express');
var path = require('path');
var fs = require('fs');
var http = require('http');
var EventEmitter = require('events').EventEmitter;
var util = require('./util');
var logger = require('./util/logger');
var rules = require('./rules');
var setupHttps = require('./https').setup;
var httpsUtil = require('./https/util');
var rulesUtil = require('./rules/util');
var initDataServer = require('./util/data-server');
var initLogServer = require('./util/log-server');
var pluginMgr = require('./plugins');
var config = require('./config');
var loadService = require('./service');
var initSocketMgr = require('./socket-mgr');
var tunnelProxy = require('./tunnel');
var upgradeProxy = require('./upgrade');

function proxy(callback) {
  var app = express();
  var server = http.createServer();
  var proxyEvents = new EventEmitter();
  var middlewares = ['./init', '../biz']
    .concat(require('./inspectors'))
    .concat(config.middlewares)
    .concat(require('./handlers'));
  server.timeout = server.keepAliveTimeout = config.idleTimeout;
  proxyEvents.config = config;
  app.logger = logger;
  middlewares.forEach(function(mw) {
    mw && app.use((typeof mw == 'string' ? require(mw) : mw).bind(proxyEvents));
  });
  server.on('clientError', function(err, socket) {
    if (!socket.writable) {
      return socket.destroy(err);
    }
    var stack = util.getErrorStack(err);
    socket.end('HTTP/1.1 400 Bad Request\r\n\r\n' + stack);
  });
  initSocketMgr(proxyEvents);
  setupHttps(proxyEvents);
  exportInterfaces(proxyEvents);
  tunnelProxy(server, proxyEvents);
  upgradeProxy(server);
  initDataServer(proxyEvents);
  initLogServer(proxyEvents);
  var properties = rulesUtil.properties;
  if (config.disableAllRules) {
    properties.set('disabledAllRules', true);
  } else if (config.disableAllRules === false) {
    properties.set('disabledAllRules', false);
  }
  if (config.disableAllPlugins) {
    properties.set('disabledAllPlugins', true);
  } else if (config.disableAllPlugins === false) {
    properties.set('disabledAllPlugins', false);
  }
  if (config.allowMultipleChoice) {
    properties.set('allowMultipleChoice', true);
  } else if (config.allowMultipleChoice === false) {
    properties.set('allowMultipleChoice', false);
  }
  rulesUtil.addValues(config.values, config.replaceExistValue);
  rulesUtil.addRules(config.rules, config.replaceExistRule);
  config.debug && rules.disableDnsCache();
  var count = 2;
  var execCallback = function() {
    if (--count === 0 && typeof callback === 'function') {
      callback.call(server, proxyEvents);
    }
  };
  if (config.host) {
    server.listen(config.port, config.host, execCallback);
  } else {
    server.listen(config.port, execCallback);
  }
  require('../biz/init')(proxyEvents, function(){
    server.on('request', app);
    execCallback();
  });
  return proxyEvents;
}

function exportInterfaces(obj) {
  obj.rules = rules;
  obj.util = util;
  obj.rulesUtil = rulesUtil;
  obj.httpsUtil = httpsUtil;
  obj.pluginMgr = pluginMgr;
  obj.logger = logger;
  obj.loadService = loadService;
  obj.setAuth = config.setAuth;
  obj.socketMgr = initSocketMgr;
  return obj;
}

process.on('uncaughtException', function(err){
  var stack = util.getErrorStack(err);
  fs.writeFileSync(path.join(process.cwd(), config.name + '.log'), '\r\n' + stack + '\r\n', {flag: 'a'});
  /*eslint no-console: "off"*/
  console.error(stack);
  process.exit(1);
});

module.exports = exportInterfaces(proxy);
<|endoftext|>


<commit_before>/**
 * @projectName fast-csv
 * @github https://github.com/C2FO/fast-csv
 * @includeDoc [Test Coverage] [../docs-md/coverage.html]
 * @header
 *
 * [![build status](https://secure.travis-ci.org/C2FO/fast-csv.png)](http://travis-ci.org/C2FO/fast-csv)
 * # Fast-csv
 *
 * This is a library is aimed at providing fast CSV parsing. It accomplishes this by not handling some of the more complex
 * edge cases such as multi line rows. However it does support escaped values, embedded commas, double and single quotes.
 *
 * ## Installation
 *
 * `npm install fast-csv`
 *
 * ## Usage
 *
 * To parse a file.
 *
 * ```javascript
 * var csv = require("fast-csv");
 *
 * csv("my.csv")
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 * ```
 *
 * You may also parse a stream.
 *
 * ```javascript
 * var stream = fs.createReadStream("my.csv");
 *
 * csv(stream)
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 *
 * ```
 *
 * If you expect the first line your csv to headers you may pass a headers option in. Setting the headers option will
 * cause change each row to an object rather than an array.
 *
 * ```javascript
 * var stream = fs.createReadStream("my.csv");
 *
 * csv(stream, {headers : true})
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 *
 * ```
 *
 * You may alternatively pass an array of header names which must match the order of each column in the csv, otherwise
 * the data columns will not match.
 *
 * ```javascript
 * var stream = fs.createReadStream("my.csv");
 *
 * csv(stream, {headers : ["firstName", "lastName", "address"]})
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 *
 * ```
 *
 * If your data may include empty rows, the sort Excel might include at the end of the file for instance, you can ignore
 * these by including the `ignoreEmpty` option.
 *
 * Any rows consisting of nothing but empty strings and/or commas will be skipped, without emitting a 'data' or 'error' event.
 *
 * ```javascript
 * var stream = fs.createReadStream("my.csv");
 *
 * csv(stream, {ignoreEmpty: true})
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 *
 * ```
 *
 * ### Validating
 *
 * You can validate each row in the csv by providing a validate handler. If a row is invalid then a `data-invalid` event
 * will be emitted with the row and the index.
 *
 * ```javascript
 * var stream = fs.createReadStream("my.csv");
 *
 * csv(stream, {headers : true})
 *  .validate(function(data){
 *      return data.age < 50; //all persons must be under the age of 50
 *  })
 *  .on("data-invalid", function(data){
 *      //do something with invalid row
 *  })
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 *
 * ```
 *
 * ### Transforming
 *
 * You can transform data by providing in a transform function. What is returned from the transform function will
 * be provided to validate and emitted as a row.
 *
 * ```javascript
 * var stream = fs.createReadStream("my.csv");
 *
 * csv(stream)
 *  .transform(function(data){
 *      return data.reverse(); //reverse each row.
 *  })
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 *
 * ```
 * @footer
 *
 * ## License
 *
 * MIT <https://github.com/C2FO/fast-csv/raw/master/LICENSE>
 *
 * ##Meta
 * * Code: `git clone git://github.com/C2FO/fast-csv.git`
 * * Website: <http://c2fo.com>
 * * Twitter: [http://twitter.com/c2fo](http://twitter.com/c2fo) - 877.465.4045
 */

var fs = require("fs"),
    _ = require("extended")().register(require("is-extended")).register(require("object-extended")),
    EventEmitter = require("events").EventEmitter,
    util = require("util"),
    out = process.stdout,
    Stream = require("stream").Stream;

var VALIDATE = /^\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*(?:,\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*)*$/;
var EMPTY = /^\s*(?:''|"")?\s*(?:,\s*(?:''|"")?\s*)*$/;
var VALUE = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g;
var LINE_SPLIT = /[\r\n|\r|\n]/;

function Parser(options) {
    EventEmitter.call(this);
    this._parsedHeaders = false;
    this._rowCount = 0;
    options = options || {};
    this._headers = options.headers;
    this._ignoreEmpty = options.ignoreEmpty;
}
util.inherits(Parser, EventEmitter);

_(Parser).extend({
    __parseLine: function __parseLineData(data, index, ignore) {
        // Return NULL if input string is not well formed CSV string.
        //regexp based on http://stackoverflow.com/a/8497474
        var ignoreEmpty = this._ignoreEmpty;
        if (!VALIDATE.test(data)) {
            this.emit("error", new Error("Invalid row " + data));
            return null;
        } else if (_.isBoolean(ignoreEmpty) && ignoreEmpty && EMPTY.test(data)) {
            return null;
        }
        var a = [];
        data.replace(VALUE, function lineReplace(m0, m1, m2, m3) {
            var item;
            if (m1 !== undefined) {
                item = m1.replace(/\\'/g, "'");
            } else if (m2 !== undefined) {
                item = m2.replace(/\\"/g, '"');
            } else if (m3 !== undefined) {
                item = m3;
            }
            a.push(item);
            return ''; // Return empty string.
        }.bind(this));
        // Handle special case of empty last value.
        if (/,\s*$/.test(data)) {
            a.push('');
        }
        if (!ignore) {
            a = this._transform(a, index);
            if (this._validate(a, index)) {
                return a;
            } else {
                this.emit("data-invalid", a, index);
            }
        } else {
            return a;
        }
    },

    _parse: function _parseLine(data) {
        var row, parseLine = this.__parseLine.bind(this), emitRow = this.emit.bind(this, "data"), count = 0;
        if (!this._parsedHeaders) {
            var headers = this._headers;
            if (_.isBoolean(headers) && headers) {
                headers = parseLine(data.shift(), 0, true);
            }
            if (_.isArray(headers)) {
                var headersLength = headers.length,
                    orig = this._transform.bind(this);
                this._transform = function (data, index) {
                    var ret = {};
                    for (var i = 0; i < headersLength; i++) {
                        ret[headers[i]] = data[i];
                    }
                    return orig(ret, index);
                };
            }
            this._parsedHeaders = true;
        }
        for (var i = 0, l = data.length; i < l; i++) {
            row = data[i];
            if (row) {
                var dataRow = parseLine(row, count);
                if (dataRow) {
                    count = this._rowCount++;
                    emitRow(dataRow, count);
                }
            }
        }
    },
    from: function _from(from) {
        this.__from = from;
        return this;
    },
    parse: function _parse(from) {
        from = from || this.__from;
        if (_.isString(from)) {
            from = fs.createReadStream(from);
            from.on("end", function () {
                from.destroy();
            });
        }
        if (_.isObject(from) && from instanceof Stream) {
            var lines = "", parse = this._parse.bind(this), end = this.emit.bind(this, "end");
            from.on("data", function streamOnData(data) {
                var lineData = (lines + data).trim().split(LINE_SPLIT);
                if (lineData.length > 1) {
                    lines = lineData.pop();
                    parse(lineData);
                } else {
                    lines += data;
                }
            });
            from.on("end", function streamOnEnd() {
                parse(lines.split(LINE_SPLIT));
                end(this._rowCount);
            }.bind(this));
        } else {
            throw new TypeError("fast-csv.Parser#parse from must be a path or ReadableStream");
        }
        return this;
    },

    _validate: function (data, index) {
        return true;
    },
    _transform: function (data, index) {
        return data;
    },
    validate: function (cb) {
        if (!_.isFunction(cb)) {
            throw new TypeError("fast-csv.Parser#validate requires a function");
        }
        this._validate = cb;
        return this;
    },
    transform: function (cb) {
        if (!_.isFunction(cb)) {
            throw new TypeError("fast-csv.Parser#transform requires a function");
        }
        this._transform = cb;
        return this;
    }
});

/**
 * Entry point to `fast-csv`. `fast-csv` does not store rows its proccesses each row and emits it. If you wish to save
 * every row into an array you must store them yourself by using the `data` event. Once all rows are done processing
 * the `end` event is emitted.
 *
 * Invoke to parse a csv file.
 *
 * @name fast-csv
 * @param location
 * @param options
 * @return {*}
 */
module.exports = function parse(location, options) {
    return new Parser(options).from(location);
};
<commit_msg>Fix missed line splits<commit_after>/**
 * @projectName fast-csv
 * @github https://github.com/C2FO/fast-csv
 * @includeDoc [Test Coverage] [../docs-md/coverage.html]
 * @header
 *
 * [![build status](https://secure.travis-ci.org/C2FO/fast-csv.png)](http://travis-ci.org/C2FO/fast-csv)
 * # Fast-csv
 *
 * This is a library is aimed at providing fast CSV parsing. It accomplishes this by not handling some of the more complex
 * edge cases such as multi line rows. However it does support escaped values, embedded commas, double and single quotes.
 *
 * ## Installation
 *
 * `npm install fast-csv`
 *
 * ## Usage
 *
 * To parse a file.
 *
 * ```javascript
 * var csv = require("fast-csv");
 *
 * csv("my.csv")
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 * ```
 *
 * You may also parse a stream.
 *
 * ```javascript
 * var stream = fs.createReadStream("my.csv");
 *
 * csv(stream)
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 *
 * ```
 *
 * If you expect the first line your csv to headers you may pass a headers option in. Setting the headers option will
 * cause change each row to an object rather than an array.
 *
 * ```javascript
 * var stream = fs.createReadStream("my.csv");
 *
 * csv(stream, {headers : true})
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 *
 * ```
 *
 * You may alternatively pass an array of header names which must match the order of each column in the csv, otherwise
 * the data columns will not match.
 *
 * ```javascript
 * var stream = fs.createReadStream("my.csv");
 *
 * csv(stream, {headers : ["firstName", "lastName", "address"]})
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 *
 * ```
 *
 * If your data may include empty rows, the sort Excel might include at the end of the file for instance, you can ignore
 * these by including the `ignoreEmpty` option.
 *
 * Any rows consisting of nothing but empty strings and/or commas will be skipped, without emitting a 'data' or 'error' event.
 *
 * ```javascript
 * var stream = fs.createReadStream("my.csv");
 *
 * csv(stream, {ignoreEmpty: true})
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 *
 * ```
 *
 * ### Validating
 *
 * You can validate each row in the csv by providing a validate handler. If a row is invalid then a `data-invalid` event
 * will be emitted with the row and the index.
 *
 * ```javascript
 * var stream = fs.createReadStream("my.csv");
 *
 * csv(stream, {headers : true})
 *  .validate(function(data){
 *      return data.age < 50; //all persons must be under the age of 50
 *  })
 *  .on("data-invalid", function(data){
 *      //do something with invalid row
 *  })
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 *
 * ```
 *
 * ### Transforming
 *
 * You can transform data by providing in a transform function. What is returned from the transform function will
 * be provided to validate and emitted as a row.
 *
 * ```javascript
 * var stream = fs.createReadStream("my.csv");
 *
 * csv(stream)
 *  .transform(function(data){
 *      return data.reverse(); //reverse each row.
 *  })
 *  .on("data", function(data){
 *      console.log(data):
 *  })
 *  .on("end", function(){
 *      console.log("done");
 *  })
 *  .parse();
 *
 * ```
 * @footer
 *
 * ## License
 *
 * MIT <https://github.com/C2FO/fast-csv/raw/master/LICENSE>
 *
 * ##Meta
 * * Code: `git clone git://github.com/C2FO/fast-csv.git`
 * * Website: <http://c2fo.com>
 * * Twitter: [http://twitter.com/c2fo](http://twitter.com/c2fo) - 877.465.4045
 */

var fs = require("fs"),
    _ = require("extended")().register(require("is-extended")).register(require("object-extended")),
    EventEmitter = require("events").EventEmitter,
    util = require("util"),
    out = process.stdout,
    Stream = require("stream").Stream;

var VALIDATE = /^\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*(?:,\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*)*$/;
var EMPTY = /^\s*(?:''|"")?\s*(?:,\s*(?:''|"")?\s*)*$/;
var VALUE = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g;
var LINE_SPLIT = /[\r\n|\r|\n]/;

function Parser(options) {
    EventEmitter.call(this);
    this._parsedHeaders = false;
    this._rowCount = 0;
    options = options || {};
    this._headers = options.headers;
    this._ignoreEmpty = options.ignoreEmpty;
}
util.inherits(Parser, EventEmitter);

_(Parser).extend({
    __parseLine: function __parseLineData(data, index, ignore) {
        // Return NULL if input string is not well formed CSV string.
        //regexp based on http://stackoverflow.com/a/8497474
        var ignoreEmpty = this._ignoreEmpty;
        if (!VALIDATE.test(data)) {
            this.emit("error", new Error("Invalid row " + data));
            return null;
        } else if (_.isBoolean(ignoreEmpty) && ignoreEmpty && EMPTY.test(data)) {
            return null;
        }
        var a = [];
        data.replace(VALUE, function lineReplace(m0, m1, m2, m3) {
            var item;
            if (m1 !== undefined) {
                item = m1.replace(/\\'/g, "'");
            } else if (m2 !== undefined) {
                item = m2.replace(/\\"/g, '"');
            } else if (m3 !== undefined) {
                item = m3;
            }
            a.push(item);
            return ''; // Return empty string.
        }.bind(this));
        // Handle special case of empty last value.
        if (/,\s*$/.test(data)) {
            a.push('');
        }
        if (!ignore) {
            a = this._transform(a, index);
            if (this._validate(a, index)) {
                return a;
            } else {
                this.emit("data-invalid", a, index);
            }
        } else {
            return a;
        }
    },

    _parse: function _parseLine(data) {
        var row, parseLine = this.__parseLine.bind(this), emitRow = this.emit.bind(this, "data"), count = 0;
        if (!this._parsedHeaders) {
            var headers = this._headers;
            if (_.isBoolean(headers) && headers) {
                headers = parseLine(data.shift(), 0, true);
            }
            if (_.isArray(headers)) {
                var headersLength = headers.length,
                    orig = this._transform.bind(this);
                this._transform = function (data, index) {
                    var ret = {};
                    for (var i = 0; i < headersLength; i++) {
                        ret[headers[i]] = data[i];
                    }
                    return orig(ret, index);
                };
            }
            this._parsedHeaders = true;
        }
        for (var i = 0, l = data.length; i < l; i++) {
            row = data[i];
            if (row) {
                var dataRow = parseLine(row, count);
                if (dataRow) {
                    count = this._rowCount++;
                    emitRow(dataRow, count);
                }
            }
        }
    },
    from: function _from(from) {
        this.__from = from;
        return this;
    },
    parse: function _parse(from) {
        from = from || this.__from;
        if (_.isString(from)) {
            from = fs.createReadStream(from);
            from.on("end", function () {
                from.destroy();
            });
        }
        if (_.isObject(from) && from instanceof Stream) {
            var lines = "", parse = this._parse.bind(this), end = this.emit.bind(this, "end");
            from.on("data", function streamOnData(data) {
                var lineData = (lines + data).split(LINE_SPLIT);
                if (lineData.length > 1) {
                    lines = lineData.pop();
                    parse(lineData);
                } else {
                    lines += data;
                }
            });
            from.on("end", function streamOnEnd() {
                parse(lines.split(LINE_SPLIT));
                end(this._rowCount);
            }.bind(this));
        } else {
            throw new TypeError("fast-csv.Parser#parse from must be a path or ReadableStream");
        }
        return this;
    },

    _validate: function (data, index) {
        return true;
    },
    _transform: function (data, index) {
        return data;
    },
    validate: function (cb) {
        if (!_.isFunction(cb)) {
            throw new TypeError("fast-csv.Parser#validate requires a function");
        }
        this._validate = cb;
        return this;
    },
    transform: function (cb) {
        if (!_.isFunction(cb)) {
            throw new TypeError("fast-csv.Parser#transform requires a function");
        }
        this._transform = cb;
        return this;
    }
});

/**
 * Entry point to `fast-csv`. `fast-csv` does not store rows its proccesses each row and emits it. If you wish to save
 * every row into an array you must store them yourself by using the `data` event. Once all rows are done processing
 * the `end` event is emitted.
 *
 * Invoke to parse a csv file.
 *
 * @name fast-csv
 * @param location
 * @param options
 * @return {*}
 */
module.exports = function parse(location, options) {
    return new Parser(options).from(location);
};
<|endoftext|>


<commit_before>const task = require('generator-runner');
const hyperquest = require('hyperquest');
const concat = require('concat-stream');
const fs = require('fs');
const path = require('path');
const cp = require('child_process');
const semver = require('semver');
const tmp = require('tmp');
const minimatch = require('minimatch');

check({
  currentVersion: '1.0.0',
  distUrl: 'https://iojs.org/dist',
  *approveFilename(filename) { return minimatch(filename, '**darwin*tar.gz'); },
  //*approveVersion(version) { return true }
}, (err, ret) => {
  if (err) console.error(`error: ${err.stack}`);
  else console.log(`return: ${ret}`);
});

class Cleanup {
  constructor() {
    this.todo = null;
    this.finished = false;
  }

  push(fn) {
    if (this.finished) {
      fn();
    }
  }

  finish(fn) {
    this.finished = true;
    if (this.todo != null) {
      for (let action of this.todo) {
        action();
      }
      fn();
    }
    else {
      this.todo = [fn];
    }
  }
}

function check({
    currentVersion,
    distUrl,
    approveFilename,
    approveVersion = function* () { return true; }
  }, cb) {
  task(function* () {
    let cleanup = new Cleanup();
    try {
      let {tmp_dir, version_array} = yield {
        tmp_dir: _ => tmp.dir({
	  unsafeCleanup:true
	}, (err, path, dispose) => {
	  cleanup.push(dispose);
	  _(err, path);
	}),
        version_array: getVersionsFromDist(distUrl, currentVersion, approveVersion)
      }
      if (version_array.length === 0) {
        return null;
      }

      let best_version = yield getBestVersion(version_array);

      // gc
      version_array = null;

      let version_url = `${distUrl}/${best_version.version}`;

      // write encrypted key to key_file
      let key_url = `${version_url}/SHASUMS256.txt.gpg`;
      let key_file = path.join(tmp_dir, 'key.gpg');
      let keyring = path.join(tmp_dir, 'keyring');

      let cleartext_sig_url = `${version_url}/SHASUMS256.txt`;

      let {cleartext_sig_body} = yield {
        keyring_imported: downloadAndImportKey(key_url, key_file, keyring),
        cleartext_sig_body: downloadAsString(cleartext_sig_url)
      };

      // download encrypted signatures and transform them to cleartext
      let encrypted_sig_url = `${version_url}/SHASUMS256.txt.asc`;
      let child = cp.spawn('gpg', ['--no-default-keyring', '--primary-keyring', keyring]);
      hyperquest(encrypted_sig_url).pipe(child.stdin);
      let {code, decrypted_sig_body} = yield {
	code: waitOnChild(child, 'gpg'),
	decrypted_sig_body: concatStream(child.stdout)
      };

      if (decrypted_sig_body !== cleartext_sig_body) {
	throw new Error('signature decryption did not match cleartext');
      }
      // gc
      cleartext_sig_body = null;

      let signatures = decrypted_sig_body.trim().split(/\n/g).map(line => {
	let [checksum, filepath] = line.trim().split(/\s+/);
	return {checksum, filepath};
      });

      let downloadable_files = yield getApprovedSignatures(signatures, approveFilename);
      if (downloadable_files.length === 0) {
        throw new Error('no file was approved to download');
      }

      let resource_file = path.join(tmp_dir, 'resource');
      for (let download of downloadable_files) {
	let download_url = `${version_url}/${download.filepath}`
	let download_file_stream = fs.createWriteStream(resource_file);

        yield waitOnStreamOpen(download_file_stream);

	// shell out because crypto cannot update list of algorithms (future safety)
	let checksum_child = cp.spawn('shasum', ['-a', '256', '-']);
	let download_http_stream = hyperquest(download_url);
	download_http_stream.pipe(checksum_child.stdin);
	download_http_stream.pipe(download_file_stream);

	let {checksum_body} = yield {
	  download: waitOnWriteStream(download_file_stream),
	  checksum_code: waitOnChild(checksum_child, 'shasum'),
	  checksum_body: concatStream(checksum_child.stdout)
	};

	if (checksum_body.split(/\s+/)[0] !== download.checksum) {
	  throw new Error(`checksum mismatch on ${download_url}`);
	}

	let final_stream = fs.createReadStream(resource_file);

        yield waitOnStreamOpen(final_stream);

	return {
	  from: download_url,
	  stream: final_stream,
	  checksum: download.checksum
	};
      }
    }
    finally {
      cleanup.finish();
    }
  }, cb);
}

function* downloadAsString(url) {
  let stream = hyperquest(url);
  return yield concatStream(stream);
}

function waitOnChild(child, name = 'child process') {
  return _ => {
    child.on('exit', (code, signal) => {
      if (code) _(new Error(`${name} failed with code: ${code}`));
      else if (signal) _(new Error(`${name} failed with signal: ${signal}`));
      else _(null);
    });
  }
}
function waitOnWriteStream(stream) {
  return _ => {
    stream
    .on('finish', () => _(null, null) )
    .on('error', err => _(err, undefined));
  }
}

function concatStream(stream) {
  return _ => {
    stream.pipe(concat(
      (body) => _(null, String(body))
    ))
    .on('error', (err) => _(err, undefined));
  }
}

function waitOnStreamOpen(stream) {
  return _ => {
    function onerror(e) {
      _(e, undefined);
    }
    function onopen() {
      stream.removeListener('error', onerror);
      _(null, undefined);
    }
    stream.on('open', onopen);
    stream.on('error', onerror);
  }
}

function* getVersionsFromDist(distUrl, currentVersion, approveVersion) {
  let body = yield downloadAsString(`${distUrl}/index.json`);
  let approved = [];
  let versions = JSON.parse(body);
  for (let version of versions) {
    let approval = yield approveVersion(version);
    let newer = semver.gt(version.version, currentVersion)
    if (approval && newer) {
      approved.push(version);
    }
  }
  return approved;
}

function* getBestVersion(versionArray, approveVersion) {
  let best_version = versionArray[0];
  let best_version_str = best_version.version.slice(1);

  for (let version of versionArray) {
    let version_str = version.version.slice(1);
    if (semver.gt(version_str, best_version_str)) {
      best_version = version;
      best_version_str = version_str;
    }
  }

  return best_version;
}

function* getApprovedSignatures(signatures, approveFilename) {
  let approved_signatures = [];
  for (let signature of signatures) {
    let approved = yield approveFilename(signature.filepath);
    if (approved) {
      approved_signatures.push(signature);
    }
  }

  return approved_signatures;
}

function* downloadAndImportKey(key_url, key_file, keyring) {
  let key_body = yield downloadAsString(key_url);
  yield _ => fs.writeFile(key_file, key_body, _);
  // gc
  key_body = null;

  // create a pgp keyring we can use with the key
  yield _ => cp.exec(`gpg --no-default-keyring --primary-keyring ${keyring} --import ${key_file}`, _);
}

function withTmpDir() {
}
<commit_msg>cleanup, move things to fns<commit_after>const task = require('generator-runner');
const hyperquest = require('hyperquest');
const concat = require('concat-stream');
const fs = require('fs');
const path = require('path');
const cp = require('child_process');
const semver = require('semver');
const tmp = require('tmp');
const minimatch = require('minimatch');

/*
check({
  currentVersion: '1.0.0',
  distUrl: 'https://iojs.org/dist',
  *approveFilename(filename) { return minimatch(filename, '**darwin*tar.gz'); },
  //*approveVersion(version) { return true }
}, (err, ret) => {
  if (err) console.error(`error: ${err.stack}`);
  else console.log(`return: ${ret}`);
});
*/

class Cleanup {
  constructor() {
    this.todo = null;
    this.finished = false;
  }

  add(fn) {
    if (this.finished) {
      fn();
    }
    if (this.todo != null) {
      this.todo.push(fn);
    }
    else {
      this.todo = [fn];
    }
  }

  finish() {
    if (this.finished) {
      throw new Error('already finished');
    }
    this.finished = true;
    for (let action of this.todo) {
      action();
    }
    this.todo = null;
  }
}

function check({
    currentVersion,
    distUrl,
    approveFilename,
    approveVersion = function* () { return true; }
  }, cb) {
  task(function* () {
    let cleanup = new Cleanup();
    try {
      let {tmp_dir, best_version} = yield {
        tmp_dir: _ => tmp.dir({
	  unsafeCleanup:true
	}, (err, path, dispose) => {
	  cleanup.add(dispose);
	  _(err, path);
	}),
        best_version: getVersionFromDist(distUrl, currentVersion, approveVersion)
      }

      let version_url = `${distUrl}/${best_version.version}`;

      // gc
      best_version = null;

      // write encrypted key to key_file
      let key_url = `${version_url}/SHASUMS256.txt.gpg`;
      let key_file = path.join(tmp_dir, 'key.gpg');
      let keyring = path.join(tmp_dir, 'keyring');

      let cleartext_sig_url = `${version_url}/SHASUMS256.txt`;

      let {cleartext_sig_body} = yield {
        keyring_imported: downloadAndImportKey(key_url, key_file, keyring),
        cleartext_sig_body: downloadAsString(cleartext_sig_url)
      };

      // download encrypted signatures and transform them to cleartext
      let encrypted_sig_url = `${version_url}/SHASUMS256.txt.asc`;
      let decrypted_sig_body = yield decryptStream(hyperquest(encrypted_sig_url), keyring);

      if (decrypted_sig_body !== cleartext_sig_body) {
	throw new Error('signature decryption did not match cleartext');
      }
      // gc
      cleartext_sig_body = null;

      let signatures = decrypted_sig_body.trim().split(/\n/g).map(line => {
	let [checksum, filepath] = line.trim().split(/\s+/);
	return {checksum, filepath};
      });

      let downloadable_files = yield getApprovedSignatures(signatures, approveFilename);
      if (downloadable_files.length === 0) {
        throw new Error('no file was approved to download');
      }

      let resource_file = path.join(tmp_dir, 'resource');
      for (let download of downloadable_files) {
	let download_url = `${version_url}/${download.filepath}`
	let download_file_stream = fs.createWriteStream(resource_file);

        yield waitOnStreamOpen(download_file_stream);

	// shell out because crypto cannot update list of algorithms (future safety)
	let checksum_child = cp.spawn('shasum', ['-a', '256', '-']);
	let download_http_stream = hyperquest(download_url);
	download_http_stream.pipe(checksum_child.stdin);
	download_http_stream.pipe(download_file_stream);

	let {checksum_body} = yield {
	  download: waitOnWriteStream(download_file_stream),
	  checksum_code: waitOnChild(checksum_child, 'shasum'),
	  checksum_body: concatStream(checksum_child.stdout)
	};

	if (checksum_body.split(/\s+/)[0] !== download.checksum) {
	  throw new Error(`checksum mismatch on ${download_url}`);
	}

	let final_stream = fs.createReadStream(resource_file);

        yield waitOnStreamOpen(final_stream);

	return {
	  from: download_url,
	  stream: final_stream,
	  checksum: download.checksum
	};
      }
    }
    finally {
      cleanup.finish();
    }
  }, cb);
}

function* downloadAsString(url) {
  let stream = hyperquest(url);
  return yield concatStream(stream);
}

function waitOnChild(child, name = 'child process') {
  return _ => {
    child.on('exit', (code, signal) => {
      if (code) _(new Error(`${name} failed with code: ${code}`));
      else if (signal) _(new Error(`${name} failed with signal: ${signal}`));
      else _(null);
    });
  }
}
function waitOnWriteStream(stream) {
  return _ => {
    stream
    .on('finish', () => _(null, null) )
    .on('error', err => _(err, undefined));
  }
}

function concatStream(stream) {
  return _ => {
    stream.pipe(concat(
      (body) => _(null, String(body))
    ))
    .on('error', (err) => _(err, undefined));
  }
}

function waitOnStreamOpen(stream) {
  return _ => {
    function onerror(e) {
      _(e, undefined);
    }
    function onopen() {
      stream.removeListener('error', onerror);
      _(null, undefined);
    }
    stream.on('open', onopen);
    stream.on('error', onerror);
  }
}

function* getVersionFromDist(distUrl, currentVersion, approveVersion) {
  let body = yield downloadAsString(`${distUrl}/index.json`);
  let approved = [];
  let versions = JSON.parse(body);
  for (let version of versions) {
    let approval = yield approveVersion(version);
    let newer = semver.gt(version.version, currentVersion)
    if (approval && newer) {
      approved.push(version);
    }
  }
  if (approved.length === 0) {
    return null;
  }

  let best_version = yield getBestVersion(approved);

  return best_version;
}

function* getBestVersion(versionArray, approveVersion) {
  let best_version = versionArray[0];
  let best_version_str = best_version.version.slice(1);

  for (let version of versionArray) {
    let version_str = version.version.slice(1);
    if (semver.gt(version_str, best_version_str)) {
      best_version = version;
      best_version_str = version_str;
    }
  }

  return best_version;
}

function* getApprovedSignatures(signatures, approveFilename) {
  let approved_signatures = [];
  for (let signature of signatures) {
    let approved = yield approveFilename(signature.filepath);
    if (approved) {
      approved_signatures.push(signature);
    }
  }

  return approved_signatures;
}

function* downloadAndImportKey(key_url, key_file, keyring) {
  let key_body = yield downloadAsString(key_url);
  yield _ => fs.writeFile(key_file, key_body, _);

  // create a pgp keyring we can use with the key
  yield _ => cp.exec(`gpg --no-default-keyring --primary-keyring ${keyring} --import ${key_file}`, _);
}

function* decryptStream(stream, keyring) {
  let child = cp.spawn('gpg', ['--no-default-keyring', '--primary-keyring', keyring]);
  stream.pipe(child.stdin);
  let {code, decrypted_sig_body} = yield {
    code: waitOnChild(child, 'gpg'),
    decrypted_sig_body: concatStream(child.stdout)
  };
  return decrypted_sig_body;
}
<|endoftext|>


<commit_before>
/**
 * # Connect
 *
 * Connect is a middleware framework for node,
 * shipping with over 11 bundled middleware and a rich choice of
 * [3rd-party middlewre](https://github.com/senchalabs/connect/wiki).
 *
 * API:
 *
 *  - [connect](connect.html) general
 *  - [http](http.html) http server
 *  - [https](https.html) https server
 *
 * Middleware:
 *
 *  - [logger](middleware-logger.html) request logger with custom format support
 *  - [basicAuth](middleware-basicAuth.html) basic http authentication
 *  - [bodyParser](middleware-bodyParser.html) extensible request body parser
 *  - [cookieParser](middleware-cookieParser.html) cookie parser
 *  - [session](middleware-session.html) session management support with bundled [MemoryStore](middleware-session-memory.html)
 *  - [compiler](middleware-compiler.html) static asset compiler (sass, less, coffee-script, etc)
 *  - [methodOverride](middleware-methodOverride.html) faux HTTP method support
 *  - [responseTime](middleware-responseTime.html) calculates response-time and exposes via X-Response-Time
 *  - [router](middleware-router.html) provides rich Sinatra / Express-like routing
 *  - [static](middleware-static.html) streaming static file server supporting `Range` and more
 *  - [vhost](middleware-vhost.html) virtual host sub-domain mapping middleware
 *  - [favicon](middleware-favicon.html) efficient favicon server (with default icon)
 *  - [limit](middleware-limit.html) limit the bytesize of request bodies
 *  - [profiler](middleware-profiler.html) request profiler reporting response-time, memory usage, etc
 *
 * Internals:
 *
 *  - connect [utilities](utils.html)
 *  - node monkey [patches](patch.html)
 *
 */<commit_msg>install docs<commit_after>
/**
 * # Connect
 *
 * Connect is a middleware framework for node,
 * shipping with over 11 bundled middleware and a rich choice of
 * [3rd-party middlewre](https://github.com/senchalabs/connect/wiki).
 *
 * Installation:
 *
 *     $ npm install connect
 *
 * API:
 *
 *  - [connect](connect.html) general
 *  - [http](http.html) http server
 *  - [https](https.html) https server
 *
 * Middleware:
 *
 *  - [logger](middleware-logger.html) request logger with custom format support
 *  - [basicAuth](middleware-basicAuth.html) basic http authentication
 *  - [bodyParser](middleware-bodyParser.html) extensible request body parser
 *  - [cookieParser](middleware-cookieParser.html) cookie parser
 *  - [session](middleware-session.html) session management support with bundled [MemoryStore](middleware-session-memory.html)
 *  - [compiler](middleware-compiler.html) static asset compiler (sass, less, coffee-script, etc)
 *  - [methodOverride](middleware-methodOverride.html) faux HTTP method support
 *  - [responseTime](middleware-responseTime.html) calculates response-time and exposes via X-Response-Time
 *  - [router](middleware-router.html) provides rich Sinatra / Express-like routing
 *  - [static](middleware-static.html) streaming static file server supporting `Range` and more
 *  - [vhost](middleware-vhost.html) virtual host sub-domain mapping middleware
 *  - [favicon](middleware-favicon.html) efficient favicon server (with default icon)
 *  - [limit](middleware-limit.html) limit the bytesize of request bodies
 *  - [profiler](middleware-profiler.html) request profiler reporting response-time, memory usage, etc
 *
 * Internals:
 *
 *  - connect [utilities](utils.html)
 *  - node monkey [patches](patch.html)
 *
 */<|endoftext|>