<commit_before>'use strict';

const { isBrowserEnvironment, loadBuildPlugins } = require('./pluginLoader');
const { indir, readdir: { sync: readdir } } = require('recur-fs');
const { strong, warn } = require('../utils/cnsl');
const filetype = require('./filetype');
const fs = require('fs');
const glob = require('glob').sync;
const match = require('minimatch');
const path = require('path');

const DEPRECATED_VERSION = 'this build format is no longer compatible with newer versions of Buddy. See https://github.com/popeindustries/buddy/blob/master/docs/config.md for help';
const MIXED_TYPE = 'mixed';
const RE_GLOB = /[\*\[\{]/;

let level, numBuildTargets;

/**
 * Parse and validate "build" section of 'config'
 * @param {Object} config
 */
module.exports = function buildParser (config) {
  if (!config.build) throw Error('missing build data');
  config.build = parseBuild(
    config.build,
    config.caches,
    config.fileExtensions,
    config.fileFactory,
    config.npmModulepaths,
    config.runtimeOptions,
    config.server
  );
};

/**
 * Parse and validate build targets
 * @param {Object} build
 * @param {Object} caches
 * @param {Object} fileExtensions
 * @param {Function} fileFactory
 * @param {Array} npmModulepaths
 * @param {Object} runtimeOptions
 * @param {Object} server
 * @param {Object} [parent]
 * @param {Object} [level]
 * @returns {Array}
 */
function parseBuild (build, caches, fileExtensions, fileFactory, npmModulepaths, runtimeOptions, server, parent, level) {
  if (!parent) {
    numBuildTargets = 0;
    level = 0;
  }

  // Deprecate sources
  if ('sources' in build) {
    warn(`${strong('sources')} attribute is no longer supported. Use environment variable ${strong('NODE_PATH')} instead`, 1);
  }
  // Deprecate targets
  if ('targets' in build) {
    warn(DEPRECATED_VERSION);
    build = build.targets;
  }

  // Support basic mode with single build target
  if (!Array.isArray(build)) build = [build];

  return build.reduce((build, buildTarget) => {
    // Deprecate old formats
    if ('js' in buildTarget || 'css' in buildTarget || 'html' in buildTarget || 'targets' in buildTarget) {
      warn(DEPRECATED_VERSION);
      return build;
    }
    // Deprecate aliases
    if (buildTarget.alias) warn(`${strong('alias')} attribute is no longer supported. Use package.json ${strong('browser')} field instead`, 1);
    // Deprecate modular
    if (buildTarget.modular) warn(`${strong('modular')} attribute has been renamed to ${strong('bundle')}`, 1);
    // Deprecate sources
    if (buildTarget.sources) warn(`${strong('sources')} attribute is no longer supported. Use environment variable ${strong('NODE_PATH')} instead`, 1);

    if (buildTarget.bootstrap == null) buildTarget.bootstrap = true;
    if (buildTarget.label == null) buildTarget.label = '';
    if (buildTarget.bundle == null || buildTarget.modular) buildTarget.bundle = true;
    buildTarget.caches = caches;
    buildTarget.hasChildren = false;
    buildTarget.hasParent = !!parent;
    buildTarget.index = ++numBuildTargets;
    buildTarget.level = level;
    buildTarget.runtimeOptions = runtimeOptions;
    buildTarget.watchOnly = !buildTarget.output;

    parseInputOutput(buildTarget, fileExtensions, runtimeOptions);
    const hasInput = 'input' in buildTarget && buildTarget.input != null;

    if (!buildTarget.hasParent) {
      let globalAliases = {};
      let pluginOptions = {};

      if (buildTarget.resolve) {
        globalAliases = buildTarget.resolve;
        delete buildTarget.resolve;
      }

      buildTarget.browser = isBrowserEnvironment(buildTarget.version);

      // Only load plugins for active output builds
      if (hasInput && !buildTarget.watchOnly) {
        // Generate plugin options based on 'options' and target 'version'
        pluginOptions = buildTarget.options = loadBuildPlugins(buildTarget.type, buildTarget.options, buildTarget.version);
      }

      let fileFactoryOptions = { caches, fileExtensions, globalAliases, npmModulepaths, pluginOptions, runtimeOptions };

      // Create fileFactory function with memoized options
      fileFactoryOptions.fileFactory = function createFile (filepath) {
        return fileFactory(filepath, fileFactoryOptions);
      };

      buildTarget.fileFactory = fileFactoryOptions.fileFactory;
    } else {
      // options/version only valid for root parent build targets
      if (buildTarget.options || buildTarget.version) warn("child build targets inherit their root parent 'version' and 'options'");
      buildTarget.browser = parent.browser;
      buildTarget.fileFactory = parent.fileFactory;
    }
    delete buildTarget.options;
    delete buildTarget.version;

    // Ignore build targets with nulled input (no grep match)
    if (hasInput) {
      // Flag as app server target
      buildTarget.isAppServer = isAppServer(buildTarget.inputpaths, server);

      // Traverse child build targets
      if (buildTarget.build) {
        buildTarget.hasChildren = true;
        buildTarget.build = parseBuild(buildTarget.build, caches, fileExtensions, fileFactory, npmModulepaths, runtimeOptions, server, buildTarget, level + 1);
        parseChildInputpaths(buildTarget);
      }
      build.push(buildTarget);
    }

    return build;
  }, []);
}

/**
 * Parse input/output path(s) for 'buildTarget'
 * @param {Object} buildTarget
 * @param {Object} fileExtensions
 * @param {Object} runtimeOptions
 */
function parseInputOutput (buildTarget, fileExtensions, runtimeOptions) {
  let outputs, outputIsDirectory;

  // Parse output
  if (buildTarget.output) {
    outputs = buildTarget.output;

    if (!Array.isArray(outputs)) outputs = [outputs];

    // Use compressed if specified
    if (runtimeOptions.compress && 'output_compressed' in buildTarget) {
      let outputsCompressed = buildTarget.output_compressed;

      if (!Array.isArray(outputsCompressed)) outputsCompressed = [outputsCompressed];
      if (outputsCompressed.length != outputs.length) {
        throw Error(`total number of outputs (${strong(buildTarget.output)}) do not match total number of compressed outputs (${strong(buildTarget.output_compressed)})`);
      }
      outputs = outputsCompressed;
      buildTarget.output = buildTarget.output_compressed;
    }

    outputIsDirectory = (outputs.length == 1 && !path.extname(outputs[0]).length);
  } else {
    buildTarget.outputpaths = [];
  }

  // Parse input
  if (buildTarget.input) {
    let allFileExtensions = [];
    let inputs = buildTarget.input;
    let inputsRelative = [];
    let type = '';

    // Gather all extensions to allow for easier filtering
    for (const type in fileExtensions) {
      allFileExtensions.push(...fileExtensions[type]);
    }

    if (!Array.isArray(inputs)) inputs = [inputs];

    buildTarget.inputpaths = inputs.reduce((inputs, input) => {
      // Expand glob pattern
      if (RE_GLOB.test(input)) {
        inputs = inputs.concat(glob(input, { matchBase: true }));
      } else {
        inputs.push(input);
      }
      return inputs;
    }, []).reduce((inputs, input) => {
      input = path.resolve(input);
      // Expand directory
      if (!path.extname(input).length) {
        // Batch mode will change output behaviour if dir -> dir
        buildTarget.batch = true;
        inputs = inputs.concat(readdir(input, (resource, stat) => {
          const isFile = stat.isFile();

          // Capture relative path for dir -> dir
          if (isFile) inputsRelative.push(path.relative(input, resource));
          return isFile;
        }));
      } else {
        inputs.push(input);
        inputsRelative.push(path.basename(input));
      }
      return inputs;
    }, []).filter((input, idx) => {
      const extension = path.extname(input).slice(1);
      let include = true;

      // Include/exclude if grepping
      if (runtimeOptions.grep) {
        include = (buildTarget.label == runtimeOptions.grep)
          || match(input, runtimeOptions.grep, { matchBase: true });
        if (runtimeOptions.invert) include = !include;
      }
      // Exclude unknown files
      if (!allFileExtensions.includes(extension)) {
        warn(`${strong(extension)} is an unknown file type. You should install a language pack from npm to enable support. Search npm for "buddy-plugin" to see all available.`);
        include = false;
      }

      if (!include) inputsRelative.splice(idx, 1);
      return include;
    }).map((input) => {
      const inputpath = path.resolve(input);
      const inputType = filetype(inputpath, fileExtensions);

      type = (!type || type == inputType) ? inputType : MIXED_TYPE;

      if (!fs.existsSync(inputpath)) warn(strong(input) + ' doesn\'t exist', 1);
      return inputpath;
    });

    if (!buildTarget.inputpaths.length) {
      buildTarget.input = null;
      buildTarget.inputpaths = null;
      return;
    }

    buildTarget.type = type;

    if (outputs) {
      if (buildTarget.inputpaths.length != outputs.length && !outputIsDirectory) {
        throw Error(`unable to resolve inputs (${strong(buildTarget.input)}) with outputs (${strong(buildTarget[runtimeOptions.compress ? 'output_compressed' : 'output'])})`);
      }

      buildTarget.outputpaths = buildTarget.inputpaths.map((inputpath, idx) => {
        let outputpath = '';

        if (outputIsDirectory) {
          // Preserve relative paths when batching
          outputpath = path.join(path.resolve(outputs[0]), inputsRelative[idx]);
        } else {
          outputpath = path.resolve(outputs[idx]);
        }

        const extension = path.extname(outputpath);
        const inputType = filetype(inputpath, fileExtensions);

        // Resolve missing extension
        if (!extension) outputpath += `.${inputType}`;
        if (inputType != 'image' && extension != `.${inputType}`) outputpath = outputpath.replace(extension, `.${inputType}`);

        return outputpath;
      });
    }
  }
}

/**
 * Parse nested child input paths
 * @param {Object} buildTarget
 */
function parseChildInputpaths (buildTarget) {
  function parse (build) {
    let inputpaths = [];

    build.forEach((buildTarget) => {
      inputpaths = inputpaths.concat(buildTarget.inputpaths, buildTarget.build ? parse(buildTarget.build) : []);
    });

    return inputpaths;
  }

  buildTarget.childInputpaths = parse(buildTarget.build);
}

/**
 * Determine if 'inputpaths' contain server file
 * @param {Array} inputpaths
 * @param {Object} server
 * @returns {Boolean}
 */
function isAppServer (inputpaths, server) {
  // Test if 'p' is in 'dirs'
  function contains (dirs, p) {
    if (!Array.isArray(dirs)) dirs = [dirs];
    return dirs.some((dir) => {
      return indir(dir, p);
    });
  }

  return server != undefined
    && server.file != undefined
    && contains(inputpaths, path.resolve(server.file));
}<commit_msg>clean paths<commit_after>'use strict';

const { isBrowserEnvironment, loadBuildPlugins } = require('./pluginLoader');
const { indir, readdir: { sync: readdir } } = require('recur-fs');
const { strong, warn } = require('../utils/cnsl');
const filetype = require('./filetype');
const fs = require('fs');
const glob = require('glob').sync;
const match = require('minimatch');
const path = require('path');

const DEPRECATED_VERSION = 'this build format is no longer compatible with newer versions of Buddy. See https://github.com/popeindustries/buddy/blob/master/docs/config.md for help';
const MIXED_TYPE = 'mixed';
const RE_GLOB = /[\*\[\{]/;

let level, numBuildTargets;

/**
 * Parse and validate "build" section of 'config'
 * @param {Object} config
 */
module.exports = function buildParser (config) {
  if (!config.build) throw Error('missing build data');
  config.build = parseBuild(
    config.build,
    config.caches,
    config.fileExtensions,
    config.fileFactory,
    config.npmModulepaths,
    config.runtimeOptions,
    config.server
  );
};

/**
 * Parse and validate build targets
 * @param {Object} build
 * @param {Object} caches
 * @param {Object} fileExtensions
 * @param {Function} fileFactory
 * @param {Array} npmModulepaths
 * @param {Object} runtimeOptions
 * @param {Object} server
 * @param {Object} [parent]
 * @param {Object} [level]
 * @returns {Array}
 */
function parseBuild (build, caches, fileExtensions, fileFactory, npmModulepaths, runtimeOptions, server, parent, level) {
  if (!parent) {
    numBuildTargets = 0;
    level = 0;
  }

  // Deprecate sources
  if ('sources' in build) {
    warn(`${strong('sources')} attribute is no longer supported. Use environment variable ${strong('NODE_PATH')} instead`, 1);
  }
  // Deprecate targets
  if ('targets' in build) {
    warn(DEPRECATED_VERSION);
    build = build.targets;
  }

  // Support basic mode with single build target
  if (!Array.isArray(build)) build = [build];

  return build.reduce((build, buildTarget) => {
    // Deprecate old formats
    if ('js' in buildTarget || 'css' in buildTarget || 'html' in buildTarget || 'targets' in buildTarget) {
      warn(DEPRECATED_VERSION);
      return build;
    }
    // Deprecate aliases
    if (buildTarget.alias) warn(`${strong('alias')} attribute is no longer supported. Use package.json ${strong('browser')} field instead`, 1);
    // Deprecate modular
    if (buildTarget.modular) warn(`${strong('modular')} attribute has been renamed to ${strong('bundle')}`, 1);
    // Deprecate sources
    if (buildTarget.sources) warn(`${strong('sources')} attribute is no longer supported. Use environment variable ${strong('NODE_PATH')} instead`, 1);

    if (buildTarget.bootstrap == null) buildTarget.bootstrap = true;
    if (buildTarget.label == null) buildTarget.label = '';
    if (buildTarget.bundle == null || buildTarget.modular) buildTarget.bundle = true;
    buildTarget.caches = caches;
    buildTarget.hasChildren = false;
    buildTarget.hasParent = !!parent;
    buildTarget.index = ++numBuildTargets;
    buildTarget.level = level;
    buildTarget.runtimeOptions = runtimeOptions;
    buildTarget.watchOnly = !buildTarget.output;

    parseInputOutput(buildTarget, fileExtensions, runtimeOptions);
    const hasInput = 'input' in buildTarget && buildTarget.input != null;

    if (!buildTarget.hasParent) {
      let globalAliases = {};
      let pluginOptions = {};

      if (buildTarget.resolve) {
        globalAliases = buildTarget.resolve;
        delete buildTarget.resolve;
      }

      buildTarget.browser = isBrowserEnvironment(buildTarget.version);

      // Only load plugins for active output builds
      if (hasInput && !buildTarget.watchOnly) {
        // Generate plugin options based on 'options' and target 'version'
        pluginOptions = buildTarget.options = loadBuildPlugins(buildTarget.type, buildTarget.options, buildTarget.version);
      }

      let fileFactoryOptions = { caches, fileExtensions, globalAliases, npmModulepaths, pluginOptions, runtimeOptions };

      // Create fileFactory function with memoized options
      fileFactoryOptions.fileFactory = function createFile (filepath) {
        return fileFactory(filepath, fileFactoryOptions);
      };

      buildTarget.fileFactory = fileFactoryOptions.fileFactory;
    } else {
      // options/version only valid for root parent build targets
      if (buildTarget.options || buildTarget.version) warn("child build targets inherit their root parent 'version' and 'options'");
      buildTarget.browser = parent.browser;
      buildTarget.fileFactory = parent.fileFactory;
    }
    delete buildTarget.options;
    delete buildTarget.version;

    // Ignore build targets with nulled input (no grep match)
    if (hasInput) {
      // Flag as app server target
      buildTarget.isAppServer = isAppServer(buildTarget.inputpaths, server);

      // Traverse child build targets
      if (buildTarget.build) {
        buildTarget.hasChildren = true;
        buildTarget.build = parseBuild(buildTarget.build, caches, fileExtensions, fileFactory, npmModulepaths, runtimeOptions, server, buildTarget, level + 1);
        parseChildInputpaths(buildTarget);
      }
      build.push(buildTarget);
    }

    return build;
  }, []);
}

/**
 * Parse input/output path(s) for 'buildTarget'
 * @param {Object} buildTarget
 * @param {Object} fileExtensions
 * @param {Object} runtimeOptions
 */
function parseInputOutput (buildTarget, fileExtensions, runtimeOptions) {
  let outputs, outputIsDirectory;

  // Parse output
  if (buildTarget.output) {
    outputs = buildTarget.output;

    if (!Array.isArray(outputs)) outputs = [outputs];

    // Use compressed if specified
    if (runtimeOptions.compress && 'output_compressed' in buildTarget) {
      let outputsCompressed = buildTarget.output_compressed;

      if (!Array.isArray(outputsCompressed)) outputsCompressed = [outputsCompressed];
      if (outputsCompressed.length != outputs.length) {
        throw Error(`total number of outputs (${strong(buildTarget.output)}) do not match total number of compressed outputs (${strong(buildTarget.output_compressed)})`);
      }
      outputs = outputsCompressed;
      buildTarget.output = buildTarget.output_compressed;
    }

    outputIsDirectory = (outputs.length == 1 && !path.extname(outputs[0]).length);
  } else {
    buildTarget.outputpaths = [];
  }

  // Parse input
  if (buildTarget.input) {
    let allFileExtensions = [];
    let inputs = buildTarget.input;
    let inputsRelative = [];
    let type = '';

    // Gather all extensions to allow for easier filtering
    for (const type in fileExtensions) {
      allFileExtensions.push(...fileExtensions[type]);
    }

    if (!Array.isArray(inputs)) inputs = [inputs];

    buildTarget.inputpaths = inputs.reduce((inputs, input) => {
      // Expand glob pattern
      if (RE_GLOB.test(input)) {
        inputs = inputs.concat(glob(input, { matchBase: true }));
      } else {
        inputs.push(input);
      }
      return inputs;
    }, []).reduce((inputs, input) => {
      input = path.resolve(input);
      // Expand directory
      if (!path.extname(input).length) {
        // Batch mode will change output behaviour if dir -> dir
        buildTarget.batch = true;
        inputs = inputs.concat(readdir(input, (resource, stat) => {
          const isFile = stat.isFile();

          // Capture relative path for dir -> dir
          if (isFile) inputsRelative.push(path.relative(input, resource));
          return isFile;
        }));
      } else {
        inputs.push(input);
        inputsRelative.push(path.basename(input));
      }
      return inputs;
    }, []).filter((input, idx) => {
      const extension = path.extname(input).slice(1);
      let include = true;

      // Include/exclude if grepping
      if (runtimeOptions.grep) {
        include = (buildTarget.label == runtimeOptions.grep)
          || match(input, runtimeOptions.grep, { matchBase: true });
        if (runtimeOptions.invert) include = !include;
      }
      // Exclude unknown files
      if (!allFileExtensions.includes(extension)) {
        warn(`${strong(extension)} is an unknown file type. You should install a language pack from npm to enable support. Search npm for "buddy-plugin" to see all available.`);
        include = false;
      }

      if (!include) inputsRelative.splice(idx, 1);
      return include;
    }).map((input) => {
      const inputpath = path.resolve(input);
      const inputType = filetype(inputpath, fileExtensions);

      type = (!type || type == inputType) ? inputType : MIXED_TYPE;

      if (!fs.existsSync(inputpath)) warn(strong(input) + ' doesn\'t exist', 1);
      return inputpath.replace(/\\/g, '/');
    });

    if (!buildTarget.inputpaths.length) {
      buildTarget.input = null;
      buildTarget.inputpaths = null;
      return;
    }

    buildTarget.type = type;

    if (outputs) {
      if (buildTarget.inputpaths.length != outputs.length && !outputIsDirectory) {
        throw Error(`unable to resolve inputs (${strong(buildTarget.input)}) with outputs (${strong(buildTarget[runtimeOptions.compress ? 'output_compressed' : 'output'])})`);
      }

      buildTarget.outputpaths = buildTarget.inputpaths.map((inputpath, idx) => {
        let outputpath = '';

        if (outputIsDirectory) {
          // Preserve relative paths when batching
          outputpath = path.join(path.resolve(outputs[0]), inputsRelative[idx]);
        } else {
          outputpath = path.resolve(outputs[idx]);
        }

        const extension = path.extname(outputpath);
        const inputType = filetype(inputpath, fileExtensions);

        // Resolve missing extension
        if (!extension) outputpath += `.${inputType}`;
        if (inputType != 'image' && extension != `.${inputType}`) outputpath = outputpath.replace(extension, `.${inputType}`);

        return outputpath.replace(/\\/g, '/');
      });
    }
  }
}

/**
 * Parse nested child input paths
 * @param {Object} buildTarget
 */
function parseChildInputpaths (buildTarget) {
  function parse (build) {
    let inputpaths = [];

    build.forEach((buildTarget) => {
      inputpaths = inputpaths.concat(buildTarget.inputpaths, buildTarget.build ? parse(buildTarget.build) : []);
    });

    return inputpaths;
  }

  buildTarget.childInputpaths = parse(buildTarget.build);
}

/**
 * Determine if 'inputpaths' contain server file
 * @param {Array} inputpaths
 * @param {Object} server
 * @returns {Boolean}
 */
function isAppServer (inputpaths, server) {
  // Test if 'p' is in 'dirs'
  function contains (dirs, p) {
    if (!Array.isArray(dirs)) dirs = [dirs];
    return dirs.some((dir) => {
      return indir(dir, p);
    });
  }

  return server != undefined
    && server.file != undefined
    && contains(inputpaths, path.resolve(server.file));
}<|endoftext|>


<commit_before>module.exports = function (sails) {

	/**
	 * Module dependencies.
	 */

	var _			= require( 'lodash' ),
	async			= require('async'),
	CaptainsLog		= require('../logger')(sails),
	ModuleLoader	= require('../moduleloader'),
	deepMerge		= require('../util').deepMerge;

	/**
	 * Expose Configuration loader
	 */

	return function (cb) {

		var self = this;

		async.auto({

			appConfig: function (cb) {

				// Immediately instantiate default logger in case a log-worthy event occurs
				// before the custom logger can be initialized
				sails.log = CaptainsLog();

				// If appPath not specified, use process.cwd() to get the app dir
				var userConfig = {};
				userConfig.appPath = userConfig.appPath || process.cwd();

				// Get config files (must be in /config)
				userConfig = _.extend(userConfig, ModuleLoader.aggregate({
					dirname		: userConfig.appPath + '/config',
					exclude		: ['locales', 'local.js', 'local.coffee'],
					excludeDirs: ['locales'],
					filter		: /(.+)\.(js|coffee)$/,
					identity	: false
				}));

				// TODO: Extend with environment-specific config

				// Extend with local config
				userConfig = self.build(userConfig, _.extend(userConfig, ModuleLoader.aggregate({
					dirname		: userConfig.appPath + '/config',
					filter		: /local\.(js|coffee)$/,
					identity	: false
				})));

				// Map command line options to configurations
				if (!sails.config) {}
				else if (sails.config.dev && sails.config.prod) {
					sails.log.error('You cannot specify both production AND development!');
					throw new Error('You cannot specify both production AND development!');
				} else if (sails.config.dev) {
					sails.config = {environment: 'development'};
				} else if (sails.config.prod) {
					sails.config = {environment: 'production'};
				}

				// Extend with whatever's in sails.config (CLI or env override)
				userConfig = deepMerge(userConfig, sails.config);

				// Override the environment variable so express mirrors the sails env:
				sails.log.verbose('Setting Node environment...');
				process.env['NODE_ENV'] = sails.config.environment || userConfig.environment;

				// Merge user config with defaults
				sails.config = deepMerge(sails.config, self.build(self.defaults(userConfig), userConfig));

				// Validate user config
				sails.config = self.validate(sails.config, userConfig);

				// Expose route config and policy tree, and mixin defaults
				sails.config.policies = deepMerge({ "*" : true }, _.clone(sails.config.policies));
				sails.routes = sails.config.routes || {};

				cb();
			},

			logger: ['appConfig', function (cb) {

				// Rebuild logger using app config
				sails.log = CaptainsLog(sails.config.log);
				cb();
			}],

			'package.json': ['appConfig', function (cb) {

				// Extend w/ data from Sails package.json
				var packageConfig = self.package();
				sails.version = packageConfig.version;
				sails.majorVersion = sails.version.split('.')[0].replace(/[^0-9]/g,'');
				sails.minorVersion = sails.version.split('.')[1].replace(/[^0-9]/g,'');
				sails.dependencies = packageConfig.dependencies;
				cb();
			}]

		}, function (err) {
			if (err) {
				sails.log.error('Error encountered loading config:', err);
				return cb(err);
			}
			cb();
		});
	};

};
<commit_msg>Change ModuleLoader usage to Modules to match other files (in configuration/load)<commit_after>module.exports = function (sails) {

	/**
	 * Module dependencies.
	 */

	var _			= require( 'lodash' ),
	async			= require('async'),
	CaptainsLog		= require('../logger')(sails),
	Modules			= require('../moduleloader'),
	deepMerge		= require('../util').deepMerge;

	/**
	 * Expose Configuration loader
	 */

	return function (cb) {

		var self = this;

		async.auto({

			appConfig: function (cb) {

				// Immediately instantiate default logger in case a log-worthy event occurs
				// before the custom logger can be initialized
				sails.log = CaptainsLog();

				// If appPath not specified, use process.cwd() to get the app dir
				var userConfig = {};
				userConfig.appPath = userConfig.appPath || process.cwd();

				// Get config files (must be in /config)
				userConfig = _.extend(userConfig, Modules.aggregate({
					dirname		: userConfig.appPath + '/config',
					exclude		: ['locales', 'local.js', 'local.coffee'],
					excludeDirs: ['locales'],
					filter		: /(.+)\.(js|coffee)$/,
					identity	: false
				}));

				// TODO: Extend with environment-specific config

				// Extend with local config
				userConfig = self.build(userConfig, _.extend(userConfig, Modules.aggregate({
					dirname		: userConfig.appPath + '/config',
					filter		: /local\.(js|coffee)$/,
					identity	: false
				})));

				// Map command line options to configurations
				if (!sails.config) {}
				else if (sails.config.dev && sails.config.prod) {
					sails.log.error('You cannot specify both production AND development!');
					throw new Error('You cannot specify both production AND development!');
				} else if (sails.config.dev) {
					sails.config = {environment: 'development'};
				} else if (sails.config.prod) {
					sails.config = {environment: 'production'};
				}

				// Extend with whatever's in sails.config (CLI or env override)
				userConfig = deepMerge(userConfig, sails.config);

				// Override the environment variable so express mirrors the sails env:
				sails.log.verbose('Setting Node environment...');
				process.env['NODE_ENV'] = sails.config.environment || userConfig.environment;

				// Merge user config with defaults
				sails.config = deepMerge(sails.config, self.build(self.defaults(userConfig), userConfig));

				// Validate user config
				sails.config = self.validate(sails.config, userConfig);

				// Expose route config and policy tree, and mixin defaults
				sails.config.policies = deepMerge({ "*" : true }, _.clone(sails.config.policies));
				sails.routes = sails.config.routes || {};

				cb();
			},

			logger: ['appConfig', function (cb) {

				// Rebuild logger using app config
				sails.log = CaptainsLog(sails.config.log);
				cb();
			}],

			'package.json': ['appConfig', function (cb) {

				// Extend w/ data from Sails package.json
				var packageConfig = self.package();
				sails.version = packageConfig.version;
				sails.majorVersion = sails.version.split('.')[0].replace(/[^0-9]/g,'');
				sails.minorVersion = sails.version.split('.')[1].replace(/[^0-9]/g,'');
				sails.dependencies = packageConfig.dependencies;
				cb();
			}]

		}, function (err) {
			if (err) {
				sails.log.error('Error encountered loading config:', err);
				return cb(err);
			}
			cb();
		});
	};

};
<|endoftext|>


<commit_before>'use strict';
const net = require('net');
const tls = require('tls');
const Connection = require('./connection');

function connect(options, callback) {
  if (options.family !== void 0) {
    makeConnection(options.family, options, (err, socket) => {
      if (err) {
        return callback(err, socket); // in the error case, `socket` is the originating error event name
      }

      callback(null, new Connection(socket, options));
    });

    return;
  }

  return makeConnection(6, options, (err, ipv6Socket) => {
    if (err) {
      makeConnection(4, options, (err, ipv4Socket) => {
        if (err) {
          return callback(err, ipv4Socket); // in the error case, `ipv4Socket` is the originating error event name
        }

        callback(null, new Connection(ipv4Socket, options));
      });

      return;
    }

    callback(null, new Connection(ipv6Socket, options));
  });
}

const LEGAL_SSL_SOCKET_OPTIONS = [
  'pfx',
  'key',
  'passphrase',
  'cert',
  'ca',
  'ciphers',
  'NPNProtocols',
  'ALPNProtocols',
  'servername',
  'ecdhCurve',
  'secureProtocol',
  'secureContext',
  'session',
  'minDHSize',
  'crl',
  'rejectUnauthorized'
];

function parseConnectOptions(family, options) {
  const host = typeof options.host === 'string' ? options.host : 'localhost';
  if (host.indexOf('/') !== -1) {
    return { path: host };
  }

  const result = {
    family,
    host,
    port: typeof options.port === 'number' ? options.port : 27017,
    rejectUnauthorized: false
  };

  return result;
}

function parseSslOptions(family, options) {
  const result = parseConnectOptions(family, options);

  // Merge in valid SSL options
  for (const name in options) {
    if (options[name] != null && LEGAL_SSL_SOCKET_OPTIONS.indexOf(name) !== -1) {
      result[name] = options[name];
    }
  }

  // Override checkServerIdentity behavior
  if (options.checkServerIdentity === false) {
    // Skip the identiy check by retuning undefined as per node documents
    // https://nodejs.org/api/tls.html#tls_tls_connect_options_callback
    result.checkServerIdentity = function() {
      return undefined;
    };
  } else if (typeof options.checkServerIdentity === 'function') {
    result.checkServerIdentity = options.checkServerIdentity;
  }

  // Set default sni servername to be the same as host
  if (result.servername == null) {
    result.servername = result.host;
  }

  return result;
}

function makeConnection(family, options, callback) {
  const useSsl = typeof options.ssl === 'boolean' ? options.ssl : false;
  const keepAlive = typeof options.keepAlive === 'boolean' ? options.keepAlive : true;
  let keepAliveInitialDelay =
    typeof options.keepAliveInitialDelay === 'number' ? options.keepAliveInitialDelay : 300000;
  const noDelay = typeof options.noDelay === 'boolean' ? options.noDelay : true;
  const connectionTimeout =
    typeof options.connectionTimeout === 'number' ? options.connectionTimeout : 30000;
  const socketTimeout = typeof options.socketTimeout === 'number' ? options.socketTimeout : 360000;
  const rejectUnauthorized =
    typeof options.rejectUnauthorized === 'boolean' ? options.rejectUnauthorized : true;

  if (keepAliveInitialDelay > socketTimeout) {
    keepAliveInitialDelay = Math.round(socketTimeout / 2);
  }

  let socket;
  try {
    if (useSsl) {
      socket = tls.connect(parseSslOptions(family, options));
    } else {
      socket = net.createConnection(parseConnectOptions(family, options));
    }
  } catch (err) {
    return callback(err);
  }

  socket.setKeepAlive(keepAlive, keepAliveInitialDelay);
  socket.setTimeout(connectionTimeout);
  socket.setNoDelay(noDelay);

  const errorEvents = ['error', 'close', 'timeout', 'parseError', 'connect'];
  function errorHandler(eventName) {
    return err => {
      if (err == null || err === false) err = true;
      errorEvents.forEach(event => socket.removeAllListeners(event));
      socket.removeListener('connect', connectHandler);
      callback(err, eventName);
    };
  }

  function connectHandler() {
    errorEvents.forEach(event => socket.removeAllListeners(event));
    if (socket.authorizationError && rejectUnauthorized) {
      return callback(socket.authorizationError);
    }

    socket.setTimeout(socketTimeout);
    callback(null, socket);
  }

  socket.once('error', errorHandler('error'));
  socket.once('close', errorHandler('close'));
  socket.once('timeout', errorHandler('timeout'));
  socket.once('parseError', errorHandler('parseError'));
  socket.once('connect', connectHandler);
}

module.exports = connect;
<commit_msg>refactor(connect): make return statements explicit<commit_after>'use strict';
const net = require('net');
const tls = require('tls');
const Connection = require('./connection');

function connect(options, callback) {
  if (options.family !== void 0) {
    makeConnection(options.family, options, (err, socket) => {
      if (err) {
        callback(err, socket); // in the error case, `socket` is the originating error event name
        return;
      }

      callback(null, new Connection(socket, options));
    });

    return;
  }

  return makeConnection(6, options, (err, ipv6Socket) => {
    if (err) {
      makeConnection(4, options, (err, ipv4Socket) => {
        if (err) {
          callback(err, ipv4Socket); // in the error case, `ipv4Socket` is the originating error event name
          return;
        }

        callback(null, new Connection(ipv4Socket, options));
      });

      return;
    }

    callback(null, new Connection(ipv6Socket, options));
  });
}

const LEGAL_SSL_SOCKET_OPTIONS = [
  'pfx',
  'key',
  'passphrase',
  'cert',
  'ca',
  'ciphers',
  'NPNProtocols',
  'ALPNProtocols',
  'servername',
  'ecdhCurve',
  'secureProtocol',
  'secureContext',
  'session',
  'minDHSize',
  'crl',
  'rejectUnauthorized'
];

function parseConnectOptions(family, options) {
  const host = typeof options.host === 'string' ? options.host : 'localhost';
  if (host.indexOf('/') !== -1) {
    return { path: host };
  }

  const result = {
    family,
    host,
    port: typeof options.port === 'number' ? options.port : 27017,
    rejectUnauthorized: false
  };

  return result;
}

function parseSslOptions(family, options) {
  const result = parseConnectOptions(family, options);

  // Merge in valid SSL options
  for (const name in options) {
    if (options[name] != null && LEGAL_SSL_SOCKET_OPTIONS.indexOf(name) !== -1) {
      result[name] = options[name];
    }
  }

  // Override checkServerIdentity behavior
  if (options.checkServerIdentity === false) {
    // Skip the identiy check by retuning undefined as per node documents
    // https://nodejs.org/api/tls.html#tls_tls_connect_options_callback
    result.checkServerIdentity = function() {
      return undefined;
    };
  } else if (typeof options.checkServerIdentity === 'function') {
    result.checkServerIdentity = options.checkServerIdentity;
  }

  // Set default sni servername to be the same as host
  if (result.servername == null) {
    result.servername = result.host;
  }

  return result;
}

function makeConnection(family, options, callback) {
  const useSsl = typeof options.ssl === 'boolean' ? options.ssl : false;
  const keepAlive = typeof options.keepAlive === 'boolean' ? options.keepAlive : true;
  let keepAliveInitialDelay =
    typeof options.keepAliveInitialDelay === 'number' ? options.keepAliveInitialDelay : 300000;
  const noDelay = typeof options.noDelay === 'boolean' ? options.noDelay : true;
  const connectionTimeout =
    typeof options.connectionTimeout === 'number' ? options.connectionTimeout : 30000;
  const socketTimeout = typeof options.socketTimeout === 'number' ? options.socketTimeout : 360000;
  const rejectUnauthorized =
    typeof options.rejectUnauthorized === 'boolean' ? options.rejectUnauthorized : true;

  if (keepAliveInitialDelay > socketTimeout) {
    keepAliveInitialDelay = Math.round(socketTimeout / 2);
  }

  let socket;
  try {
    if (useSsl) {
      socket = tls.connect(parseSslOptions(family, options));
    } else {
      socket = net.createConnection(parseConnectOptions(family, options));
    }
  } catch (err) {
    return callback(err);
  }

  socket.setKeepAlive(keepAlive, keepAliveInitialDelay);
  socket.setTimeout(connectionTimeout);
  socket.setNoDelay(noDelay);

  const errorEvents = ['error', 'close', 'timeout', 'parseError', 'connect'];
  function errorHandler(eventName) {
    return err => {
      if (err == null || err === false) err = true;
      errorEvents.forEach(event => socket.removeAllListeners(event));
      socket.removeListener('connect', connectHandler);
      callback(err, eventName);
    };
  }

  function connectHandler() {
    errorEvents.forEach(event => socket.removeAllListeners(event));
    if (socket.authorizationError && rejectUnauthorized) {
      return callback(socket.authorizationError);
    }

    socket.setTimeout(socketTimeout);
    callback(null, socket);
  }

  socket.once('error', errorHandler('error'));
  socket.once('close', errorHandler('close'));
  socket.once('timeout', errorHandler('timeout'));
  socket.once('parseError', errorHandler('parseError'));
  socket.once('connect', connectHandler);
}

module.exports = connect;
<|endoftext|>


<commit_before>var winston = require('winston');

var ConnectionManager = function () {
  this.connections = {};
};

ConnectionManager.prototype = {

  // Add a socket by requesting its identity and bucketing it
  add: function (socket) {
    var that = this;
    var currentIdentity;
    // Remove from any current identities and add to the new one
    socket.on('identity', function (ident) {
      if (currentIdentity) {
        that.removeSocketFromIdentity(currentIdentity, socket);
      }
      that.addSocketToIdentity(ident, socket);
      currentIdentity = ident;
    });
    // Remove from any current identity
    socket.on('disconnect', function () {
      if (currentIdentity) {
        that.removeSocketFromIdentity(currentIdentity, socket);
      }
    });
    // And ask to identify on each add
    socket.emit('identify');
  },

  // Remove a socket completely from an identity
  removeSocketFromIdentity: function (identity, socket) {
    var connections = this.connections[identity];
    if (connections) {
      for (var i = 0; i < connections.length; i++) {
        winston.debug('removed socket identity', { identity: identity, id: socket.id });
        connections.splice(i, 1);
      }
    }
  },

  // Add a socket to a given identity
  addSocketToIdentity: function (identity, socket) {
    var connections = this.connections[identity];
    if (!connections) {
      connections = this.connections[identity] = [];
    }
    winston.debug('added socket identity', { identity: identity, id: socket.id });
    connections.push(socket);
  },

  // Notify a single identity of a message
  notify: function (identity, message, data) {
    if (identity === '*') {
      return this.notifyAll(message, data);
    }
    var connections = this.connections[identity];
    if (connections) {
      for (var i = 0; i < connections.length; i++) {
        winston.debug('sending data to ' + identity + '/' + message);
        connections[i].emit(message, data);
      }
    }
  },

  // Notify all
  notifyAll: function (message, data) {
    winston.debug('sending data to ' + '*/' + message);
    this.socketServer.sockets.emit(message, data);
  }

};

module.exports = ConnectionManager;
<commit_msg>Fix a bug when removing currentIdentity<commit_after>var winston = require('winston');

var ConnectionManager = function () {
  this.connections = {};
};

ConnectionManager.prototype = {

  // Add a socket by requesting its identity and bucketing it
  add: function (socket) {
    var that = this;
    var currentIdentity;
    // Remove from any current identities and add to the new one
    socket.on('identity', function (ident) {
      if (currentIdentity) {
        that.removeSocketFromIdentity(currentIdentity, socket);
        currentIdentity = null;
      }
      that.addSocketToIdentity(ident, socket);
      currentIdentity = ident;
    });
    // Remove from any current identity
    socket.on('disconnect', function () {
      if (currentIdentity) {
        that.removeSocketFromIdentity(currentIdentity, socket);
        currentIdentity = null;
      }
    });
    // And ask to identify on each add
    socket.emit('identify');
  },

  // Remove a socket completely from an identity
  removeSocketFromIdentity: function (identity, socket) {
    var connections = this.connections[identity];
    if (connections) {
      for (var i = 0; i < connections.length; i++) {
        winston.debug('removed socket identity', { identity: identity, id: socket.id });
        connections.splice(i, 1);
      }
    }
  },

  // Add a socket to a given identity
  addSocketToIdentity: function (identity, socket) {
    var connections = this.connections[identity];
    if (!connections) {
      connections = this.connections[identity] = [];
    }
    winston.debug('added socket identity', { identity: identity, id: socket.id });
    connections.push(socket);
  },

  // Notify a single identity of a message
  notify: function (identity, message, data) {
    if (identity === '*') {
      return this.notifyAll(message, data);
    }
    var connections = this.connections[identity];
    if (connections) {
      for (var i = 0; i < connections.length; i++) {
        winston.debug('sending data to ' + identity + '/' + message);
        connections[i].emit(message, data);
      }
    }
  },

  // Notify all
  notifyAll: function (message, data) {
    winston.debug('sending data to ' + '*/' + message);
    this.socketServer.sockets.emit(message, data);
  }

};

module.exports = ConnectionManager;
<|endoftext|>


<commit_before>const moment = require('moment');
const got = require('got');
const url = require('url');
const delay = require('delay');
const getRegistryUrl = require('registry-auth-token/registry-url');
const registryAuthToken = require('registry-auth-token');
const parse = require('github-url-from-git');
const { isBase64 } = require('validator');

const hostRules = require('../../util/host-rules');
const { maskToken } = require('../../util/mask');
const { getNpmrc } = require('./npmrc');

module.exports = {
  getDependency,
  resetCache,
  resetMemCache,
};

let memcache = {};

function resetMemCache() {
  logger.debug('resetMemCache()');
  memcache = {};
}

function resetCache() {
  resetMemCache();
}

async function getDependency(name, maxRetries = 5) {
  let retries = maxRetries;
  logger.trace(`npm.getDependency(${name})`);

  // This is our datastore cache and is cleared at the end of each repo, i.e. we never requery/revalidate during a "run"
  if (memcache[name]) {
    logger.trace('Returning cached result');
    return JSON.parse(memcache[name]);
  }

  // Now check the persistent cache
  const cacheNamespace = 'datasource-npm';
  const cachedResult = await renovateCache.get(cacheNamespace, name);
  if (cachedResult) {
    return cachedResult;
  }

  const scope = name.split('/')[0];
  let regUrl;
  const npmrc = getNpmrc();
  try {
    regUrl = getRegistryUrl(scope, npmrc);
  } catch (err) {
    regUrl = 'https://registry.npmjs.org';
  }
  const pkgUrl = url.resolve(
    regUrl,
    encodeURIComponent(name).replace(/^%40/, '@')
  );
  const authInfo = registryAuthToken(regUrl, { npmrc });
  const headers = {};

  if (authInfo && authInfo.type && authInfo.token) {
    // istanbul ignore if
    if (npmrc && npmrc.massagedAuth && isBase64(authInfo.token)) {
      logger.debug('Massaging authorization type to Basic');
      authInfo.type = 'Basic';
    }
    headers.authorization = `${authInfo.type} ${authInfo.token}`;
    logger.debug(
      { token: maskToken(authInfo.token), npmName: name },
      'Using auth for npm lookup'
    );
  } else if (process.env.NPM_TOKEN && process.env.NPM_TOKEN !== 'undefined') {
    headers.authorization = `Bearer ${process.env.NPM_TOKEN}`;
  }

  if (
    pkgUrl.startsWith('https://registry.npmjs.org') &&
    !pkgUrl.startsWith('https://registry.npmjs.org/@')
  ) {
    // Delete the authorization header for non-scoped public packages to improve http caching
    // Otherwise, authenticated requests are not cacheable until the registry adds "public" to Cache-Control
    // Ref: https://greenbytes.de/tech/webdav/rfc7234.html#caching.authenticated.responses
    delete headers.authorization;
  }

  // This tells our http layer not to serve responses directly from the cache and instead to revalidate them every time
  headers['Cache-Control'] = 'no-cache';

  try {
    const raw = await got(pkgUrl, {
      json: true,
      retry: {
        retries: (retry, err) => {
          if (retries <= 0) {
            return 0;
          }
          let delayUnit = 1000;
          if (process.env.NODE_ENV === 'test') {
            delayUnit = 1;
          }
          const defaultDelay = (5 * delayUnit) / retries;
          retries -= 1;
          // istanbul ignore if
          if (
            err.code === 'ETIMEDOUT' &&
            err.url &&
            !err.url.startsWith('https://registry.npmjs.org')
          ) {
            logger.info(
              { depName: name, url: err.url },
              'Cannot connect to private npm host - skipping lookup'
            );
            return 0;
          }
          if (err.statusCode === 429) {
            const retryAfter = err.headers['retry-after'] || 30;
            logger.info(
              `npm too many requests. retrying after ${retryAfter} seconds`
            );
            return delayUnit * (retryAfter + 1);
          }
          if (err.statusCode === 408) {
            logger.info({ err }, 'npm registry failure: timeout, retrying');
            return defaultDelay;
          }
          if (err.statusCode >= 500 && err.statusCode < 600) {
            logger.info(
              { err },
              'npm registry failure: internal error, retrying'
            );
            return defaultDelay;
          }
          return 0;
        },
      },
      headers,
    });
    const res = raw.body;
    // eslint-disable-next-line no-underscore-dangle
    const returnedName = res.name ? res.name : res._id || '';
    if (returnedName.toLowerCase() !== name.toLowerCase()) {
      logger.warn(
        { lookupName: name, returnedName: res.name, regUrl },
        'Returned name does not match with requested name'
      );
      return null;
    }
    if (!res.versions || !Object.keys(res.versions).length) {
      // Registry returned a 200 OK but with no versions
      if (retries <= 0) {
        logger.info({ dependency: name }, 'No versions returned');
        return null;
      }
      logger.info('No versions returned, retrying');
      await delay(5000 / retries);
      return getDependency(name, 0);
    }

    const latestVersion = res.versions[res['dist-tags'].latest];
    res.repository = res.repository || latestVersion.repository;
    res.homepage = res.homepage || latestVersion.homepage;

    // Determine repository URL
    let sourceUrl;

    if (res.repository && res.repository.url) {
      const extraBaseUrls = [];
      // istanbul ignore next
      hostRules.hosts({ platform: 'github' }).forEach(host => {
        extraBaseUrls.push(host, `gist.${host}`);
      });
      // Massage www out of github URL
      res.repository.url = res.repository.url.replace(
        'www.github.com',
        'github.com'
      );
      if (res.repository.url.startsWith('https://github.com/')) {
        res.repository.url = res.repository.url
          .split('/')
          .slice(0, 5)
          .join('/');
      }
      sourceUrl = parse(res.repository.url, {
        extraBaseUrls,
      });
    }
    if (res.homepage && res.homepage.includes('://github.com')) {
      delete res.homepage;
    }
    // Simplify response before caching and returning
    const dep = {
      name: res.name,
      homepage: res.homepage,
      latestVersion: res['dist-tags'].latest,
      sourceUrl,
      versions: {},
      'dist-tags': res['dist-tags'],
      'renovate-config': latestVersion['renovate-config'],
    };
    if (res.repository && res.repository.directory) {
      dep.sourceDirectory = res.repository.directory;
    }
    if (latestVersion.deprecated) {
      dep.deprecationMessage = `On registry \`${regUrl}\`, the "latest" version (v${
        dep.latestVersion
      }) of dependency \`${name}\` has the following deprecation notice:\n\n\`${
        latestVersion.deprecated
      }\`\n\nMarking the latest version of an npm package as deprecated results in the entire package being considered deprecated, so contact the package author you think this is a mistake.`;
      dep.deprecationSource = 'npm';
    }
    dep.releases = Object.keys(res.versions).map(version => {
      const release = {
        version,
        gitRef: res.versions[version].gitHead,
      };
      if (res.time && res.time[version]) {
        release.releaseTimestamp = res.time[version];
        release.canBeUnpublished =
          moment().diff(moment(release.releaseTimestamp), 'days') === 0;
      }
      if (res.versions[version].deprecated) {
        release.isDeprecated = true;
      }
      return release;
    });
    logger.trace({ dep }, 'dep');
    // serialize first before saving
    memcache[name] = JSON.stringify(dep);
    const cacheMinutes = process.env.RENOVATE_CACHE_NPM_MINUTES
      ? parseInt(process.env.RENOVATE_CACHE_NPM_MINUTES, 10)
      : 5;
    if (!name.startsWith('@')) {
      await renovateCache.set(cacheNamespace, name, dep, cacheMinutes);
    }
    return dep;
  } catch (err) {
    if (err.statusCode === 401 || err.statusCode === 403) {
      logger.info(
        {
          pkgUrl,
          authInfoType: authInfo ? authInfo.type : undefined,
          authInfoToken: authInfo ? maskToken(authInfo.token) : undefined,
          err,
          statusCode: err.statusCode,
          depName: name,
        },
        `Dependency lookup failure: unauthorized`
      );
      return null;
    }
    if (err.statusCode === 404 || err.code === 'ENOTFOUND') {
      logger.info({ depName: name }, `Dependency lookup failure: not found`);
      logger.debug({
        err,
        token: authInfo ? maskToken(authInfo.token) : 'none',
      });
      return null;
    }
    if (err.name === 'ParseError') {
      // Registry returned a 200 OK but got failed to parse it
      logger.info({ err }, 'npm registry failure: ParseError, retrying');
      await delay(5000 / retries);
      return getDependency(name, retries - 1);
    }
    // istanbul ignore if
    if (err.code === 'ECONNRESET') {
      // Usually a temporary error
      logger.info({ err }, 'npm registry failure: ECONNRESET, retrying');
      await delay(5000 / retries);
      return getDependency(name, retries - 1);
    }
    logger.warn({ err, regUrl, depName: name }, 'npm registry failure');
    throw new Error('registry-failure');
  }
}
<commit_msg>logs: trace npm auth<commit_after>const moment = require('moment');
const got = require('got');
const url = require('url');
const delay = require('delay');
const getRegistryUrl = require('registry-auth-token/registry-url');
const registryAuthToken = require('registry-auth-token');
const parse = require('github-url-from-git');
const { isBase64 } = require('validator');

const hostRules = require('../../util/host-rules');
const { maskToken } = require('../../util/mask');
const { getNpmrc } = require('./npmrc');

module.exports = {
  getDependency,
  resetCache,
  resetMemCache,
};

let memcache = {};

function resetMemCache() {
  logger.debug('resetMemCache()');
  memcache = {};
}

function resetCache() {
  resetMemCache();
}

async function getDependency(name, maxRetries = 5) {
  let retries = maxRetries;
  logger.trace(`npm.getDependency(${name})`);

  // This is our datastore cache and is cleared at the end of each repo, i.e. we never requery/revalidate during a "run"
  if (memcache[name]) {
    logger.trace('Returning cached result');
    return JSON.parse(memcache[name]);
  }

  // Now check the persistent cache
  const cacheNamespace = 'datasource-npm';
  const cachedResult = await renovateCache.get(cacheNamespace, name);
  if (cachedResult) {
    return cachedResult;
  }

  const scope = name.split('/')[0];
  let regUrl;
  const npmrc = getNpmrc();
  try {
    regUrl = getRegistryUrl(scope, npmrc);
  } catch (err) {
    regUrl = 'https://registry.npmjs.org';
  }
  const pkgUrl = url.resolve(
    regUrl,
    encodeURIComponent(name).replace(/^%40/, '@')
  );
  const authInfo = registryAuthToken(regUrl, { npmrc });
  const headers = {};

  if (authInfo && authInfo.type && authInfo.token) {
    // istanbul ignore if
    if (npmrc && npmrc.massagedAuth && isBase64(authInfo.token)) {
      logger.debug('Massaging authorization type to Basic');
      authInfo.type = 'Basic';
    }
    headers.authorization = `${authInfo.type} ${authInfo.token}`;
    logger.trace(
      { token: maskToken(authInfo.token), npmName: name },
      'Using auth for npm lookup'
    );
  } else if (process.env.NPM_TOKEN && process.env.NPM_TOKEN !== 'undefined') {
    headers.authorization = `Bearer ${process.env.NPM_TOKEN}`;
  }

  if (
    pkgUrl.startsWith('https://registry.npmjs.org') &&
    !pkgUrl.startsWith('https://registry.npmjs.org/@')
  ) {
    // Delete the authorization header for non-scoped public packages to improve http caching
    // Otherwise, authenticated requests are not cacheable until the registry adds "public" to Cache-Control
    // Ref: https://greenbytes.de/tech/webdav/rfc7234.html#caching.authenticated.responses
    delete headers.authorization;
  }

  // This tells our http layer not to serve responses directly from the cache and instead to revalidate them every time
  headers['Cache-Control'] = 'no-cache';

  try {
    const raw = await got(pkgUrl, {
      json: true,
      retry: {
        retries: (retry, err) => {
          if (retries <= 0) {
            return 0;
          }
          let delayUnit = 1000;
          if (process.env.NODE_ENV === 'test') {
            delayUnit = 1;
          }
          const defaultDelay = (5 * delayUnit) / retries;
          retries -= 1;
          // istanbul ignore if
          if (
            err.code === 'ETIMEDOUT' &&
            err.url &&
            !err.url.startsWith('https://registry.npmjs.org')
          ) {
            logger.info(
              { depName: name, url: err.url },
              'Cannot connect to private npm host - skipping lookup'
            );
            return 0;
          }
          if (err.statusCode === 429) {
            const retryAfter = err.headers['retry-after'] || 30;
            logger.info(
              `npm too many requests. retrying after ${retryAfter} seconds`
            );
            return delayUnit * (retryAfter + 1);
          }
          if (err.statusCode === 408) {
            logger.info({ err }, 'npm registry failure: timeout, retrying');
            return defaultDelay;
          }
          if (err.statusCode >= 500 && err.statusCode < 600) {
            logger.info(
              { err },
              'npm registry failure: internal error, retrying'
            );
            return defaultDelay;
          }
          return 0;
        },
      },
      headers,
    });
    const res = raw.body;
    // eslint-disable-next-line no-underscore-dangle
    const returnedName = res.name ? res.name : res._id || '';
    if (returnedName.toLowerCase() !== name.toLowerCase()) {
      logger.warn(
        { lookupName: name, returnedName: res.name, regUrl },
        'Returned name does not match with requested name'
      );
      return null;
    }
    if (!res.versions || !Object.keys(res.versions).length) {
      // Registry returned a 200 OK but with no versions
      if (retries <= 0) {
        logger.info({ dependency: name }, 'No versions returned');
        return null;
      }
      logger.info('No versions returned, retrying');
      await delay(5000 / retries);
      return getDependency(name, 0);
    }

    const latestVersion = res.versions[res['dist-tags'].latest];
    res.repository = res.repository || latestVersion.repository;
    res.homepage = res.homepage || latestVersion.homepage;

    // Determine repository URL
    let sourceUrl;

    if (res.repository && res.repository.url) {
      const extraBaseUrls = [];
      // istanbul ignore next
      hostRules.hosts({ platform: 'github' }).forEach(host => {
        extraBaseUrls.push(host, `gist.${host}`);
      });
      // Massage www out of github URL
      res.repository.url = res.repository.url.replace(
        'www.github.com',
        'github.com'
      );
      if (res.repository.url.startsWith('https://github.com/')) {
        res.repository.url = res.repository.url
          .split('/')
          .slice(0, 5)
          .join('/');
      }
      sourceUrl = parse(res.repository.url, {
        extraBaseUrls,
      });
    }
    if (res.homepage && res.homepage.includes('://github.com')) {
      delete res.homepage;
    }
    // Simplify response before caching and returning
    const dep = {
      name: res.name,
      homepage: res.homepage,
      latestVersion: res['dist-tags'].latest,
      sourceUrl,
      versions: {},
      'dist-tags': res['dist-tags'],
      'renovate-config': latestVersion['renovate-config'],
    };
    if (res.repository && res.repository.directory) {
      dep.sourceDirectory = res.repository.directory;
    }
    if (latestVersion.deprecated) {
      dep.deprecationMessage = `On registry \`${regUrl}\`, the "latest" version (v${
        dep.latestVersion
      }) of dependency \`${name}\` has the following deprecation notice:\n\n\`${
        latestVersion.deprecated
      }\`\n\nMarking the latest version of an npm package as deprecated results in the entire package being considered deprecated, so contact the package author you think this is a mistake.`;
      dep.deprecationSource = 'npm';
    }
    dep.releases = Object.keys(res.versions).map(version => {
      const release = {
        version,
        gitRef: res.versions[version].gitHead,
      };
      if (res.time && res.time[version]) {
        release.releaseTimestamp = res.time[version];
        release.canBeUnpublished =
          moment().diff(moment(release.releaseTimestamp), 'days') === 0;
      }
      if (res.versions[version].deprecated) {
        release.isDeprecated = true;
      }
      return release;
    });
    logger.trace({ dep }, 'dep');
    // serialize first before saving
    memcache[name] = JSON.stringify(dep);
    const cacheMinutes = process.env.RENOVATE_CACHE_NPM_MINUTES
      ? parseInt(process.env.RENOVATE_CACHE_NPM_MINUTES, 10)
      : 5;
    if (!name.startsWith('@')) {
      await renovateCache.set(cacheNamespace, name, dep, cacheMinutes);
    }
    return dep;
  } catch (err) {
    if (err.statusCode === 401 || err.statusCode === 403) {
      logger.info(
        {
          pkgUrl,
          authInfoType: authInfo ? authInfo.type : undefined,
          authInfoToken: authInfo ? maskToken(authInfo.token) : undefined,
          err,
          statusCode: err.statusCode,
          depName: name,
        },
        `Dependency lookup failure: unauthorized`
      );
      return null;
    }
    if (err.statusCode === 404 || err.code === 'ENOTFOUND') {
      logger.info({ depName: name }, `Dependency lookup failure: not found`);
      logger.debug({
        err,
        token: authInfo ? maskToken(authInfo.token) : 'none',
      });
      return null;
    }
    if (err.name === 'ParseError') {
      // Registry returned a 200 OK but got failed to parse it
      logger.info({ err }, 'npm registry failure: ParseError, retrying');
      await delay(5000 / retries);
      return getDependency(name, retries - 1);
    }
    // istanbul ignore if
    if (err.code === 'ECONNRESET') {
      // Usually a temporary error
      logger.info({ err }, 'npm registry failure: ECONNRESET, retrying');
      await delay(5000 / retries);
      return getDependency(name, retries - 1);
    }
    logger.warn({ err, regUrl, depName: name }, 'npm registry failure');
    throw new Error('registry-failure');
  }
}
<|endoftext|>


<commit_before>'use strict';

const rp = require('request-promise');
const defaultTransitKey = Config.get('acs:transit_key');

const VAULT_CLIENT = new Vaulted({
  vault_host: Config.get('vault:host'),
  vault_port: Config.get('vault:port'),
  vault_ssl: Config.get('vault:ssh')
});

const getToken = function() {
  return rp({
    uri: 'http://localhost:4500/v1/token/default',
    json: true}
  ).then((body) => body.token);
};

module.exports = function generateCiphertext(secret) {
  return getToken().then((token) => {
    t = token;

    return VAULT_CLIENT.prepare(token);
  }).then((client) => client.encryptTransitPlainText({
    id: defaultTransitKey,
    body: {
      plaintext: secret
    },
    token: t
  })).then((response) => response.data.ciphertext);
};
<commit_msg>Fixed linting issues.<commit_after>'use strict';

const rp = require('request-promise');
const defaultTransitKey = Config.get('acs:transit_key');

const TOKEND_ENDPOINT = 'http://localhost:4500/v1/token/default';
const VAULT_ENDPOINT = `http${Config.get('vault:ssl') ? 's' : ''}://${Config.get('vault:host')}:${Config.get('vault:port')}`;

const getToken = () => rp({uri: TOKEND_ENDPOINT, json: true}).then((body) => body.token);

module.exports = function generateCiphertext(secret) {
  return getToken().then((token) => {
    const options = {
      method: 'POST',
      uri: `${VAULT_ENDPOINT}/v1/transit/encrypt/${defaultTransitKey}`,
      body: {
        plaintext: secret
      },
      headers: {
        'X-Vault-Token': token
      },
      json: true
    };

    return rp(options);
  }).then((resp) => resp.data.ciphertext);
};
<|endoftext|>


<commit_before>define(['require', 'num'], function(require, num) {
    var num = require('num'),
        AffineTransformation = num.AffineTransformation,
        Vector2 = num.Vector2;

	var Canvas = mini.Class.subclass({
		initialize: function(canvasId) {
			this.canvasId = canvasId || "engineCanvas";
			this.extent = Vector2.Zero.copy();
			/**
			 * A matrix that describes the transformation from a 1 by 1 unit square to the actual canvas extent.
			 */
			this.transformation = AffineTransformation.Identity.copy();

			if ($("canvas#" + this.canvasId).length > 0) {
				this.$element = $("canvas#" + this.canvasId);
			} else {
				this.$element = $("<canvas></canvas>")
					.attr("id", this.canvasId)
					.appendTo($("body"));
			}

			this.restore();
			this.setExtentXY(
				this.$element.width(),
				this.$element.height()
			);

			this.domElement = document.getElementById(this.canvasId);

			this.updateTransformation();
		},

		resize: function(x, y) {
			this.setExtentXY(x, y);

			this.$element
				.attr("width", this.extent.x)
				.attr("height", this.extent.y);

			if(typeof env !== "undefined" && typeof env.camera !== "undefined")
				env.camera.resetScaleRange();
		},

		setExtent: function(newExtent) {
			this.extent.set(newExtent);
			this.updateTransformation();
		},

		setExtentXY: function(x, y) {
			this.extent.setXY(x, y);
			this.updateTransformation();
		},

		updateTransformation: function() {
		    this.transformation.set(AffineTransformation.Identity);
			// move the coordinate system's origin to the middle of the canvas
            this.transformation.translateSelf(this.extent.divFloat(2));
			// rescale 1 by 1 box to canvas size
            this.transformation.scaleSelf(this.extent);
            // invert y-coordinates to flip around x-axis
            this.transformation.scaleSelf(new Vector2(1, -1));
		},

		color: function(backgroundColor) {
			this.$element.css("background-color", backgroundColor);

			return this;
		},

		restore: function() {
			this.$element
				.css("position", "absolute")
				.css("top", "0px")
				.css("left", "0px")
				.css("z-index", -1);

			if(this.callback) {
				$(window).off("resize", this.callback);
				this.callback = undefined;
			}
		},

		/*
		 * formatting methods
		 */

		// give the canvas a fixed size, independent of the window size
		fixedSize: function(width, height) {
			this.restore();
			this.resize(width, height);

			return this;
		},

		// give the canvas a fixed ratio, it will fit into the window
		screenFit: function(ratio) {
			this.restore();

			this.ratio = ratio;

			this.callback = this.fitOnResize.bind(this)
			$(window).resize(this.callback);

			this.callback();

			return this;
		},

		fitOnResize: function() {
			var windowWidth = $(window).width();
			var windowHeight = $(window).height();
			var windowRatio = windowWidth/windowHeight;

			if(this.ratio > windowRatio) {
				// adjust height of the canvas
				var canvasHeight = windowHeight * windowRatio / this.ratio;
				this.resize(windowWidth, canvasHeight);
				this.$element
					.css("top", (windowHeight - canvasHeight) / 2 + "px")
					.css("left", "0px");
			}
			else {
				// adjust width of the canvas
				var canvasWidth = windowWidth * this.ratio / windowRatio;
				this.resize(canvasWidth, windowHeight);
				this.$element
					.css("top", "0px")
					.css("left", (windowWidth - canvasWidth) / 2 + "px");
			}
		},

		// stretches the canvas to the whole window
		stretch: function() {
			this.restore();

			this.callback = this.stretchOnResize.bind(this);
			$(window).resize(this.callback);

			this.callback();

			return this;
		},

		stretchOnResize: function() {
		    this.resize($(window).width(), $(window).height());
		},

		// request a fullScreen for the canvas
		fullScreen: function() {
			this.restore();

			if (screenfull.enabled) {
			    screenfull.request(this.domElement);
			}

			var rect = this.domElement.getBoundingClientRect();
	        //this.resize(rect.width, rect.height);
			Bloob.log(document.body.clientWidth, document.body.clientHeight);
			Bloob.log(rect.width, rect.height);
			this.$element
				.css("width", "100%")
				.css("height", "100%");
			Bloob.log(this.$element.width(), this.$element.height());
			Bloob.log(this.domElement);
	        this.resize(this.$element.attr("height"), this.$element.attr("height"));

		    return this;
		}
	});

	return Canvas;
});
<commit_msg>added todos to Canvas<commit_after>define(['require', 'num'], function(require, num) {
    var num = require('num'),
        AffineTransformation = num.AffineTransformation,
        Vector2 = num.Vector2;

	var Canvas = mini.Class.subclass({
		initialize: function(canvasId) {
			this.canvasId = canvasId || "engineCanvas";
			this.extent = Vector2.Zero.copy();
			/**
			 * A matrix that describes the transformation from a 1 by 1 unit square to the actual canvas extent.
			 */
			this.transformation = AffineTransformation.Identity.copy();

			// TODO: wrap in a div
			if ($("canvas#" + this.canvasId).length > 0) {
				this.$element = $("canvas#" + this.canvasId);
			} else {
				this.$element = $("<canvas></canvas>")
					.attr("id", this.canvasId)
					.appendTo($("body"));
			}

			this.restore();
			this.setExtentXY(
				this.$element.width(),
				this.$element.height()
			);

			this.domElement = document.getElementById(this.canvasId);

			this.updateTransformation();
		},

		resize: function(x, y) {
			this.setExtentXY(x, y);

			this.$element
				.attr("width", this.extent.x)
				.attr("height", this.extent.y);

			if(typeof env !== "undefined" && typeof env.camera !== "undefined")
				env.camera.resetScaleRange();
		},

		setExtent: function(newExtent) {
			this.extent.set(newExtent);
			this.updateTransformation();
		},

		setExtentXY: function(x, y) {
			this.extent.setXY(x, y);
			this.updateTransformation();
		},

		updateTransformation: function() {
		    this.transformation.set(AffineTransformation.Identity);
			// move the coordinate system's origin to the middle of the canvas
            this.transformation.translateSelf(this.extent.divFloat(2));
			// rescale 1 by 1 box to canvas size
            this.transformation.scaleSelf(this.extent);
            // invert y-coordinates to flip around x-axis
            this.transformation.scaleSelf(new Vector2(1, -1));
		},

		color: function(backgroundColor) {
			this.$element.css("background-color", backgroundColor);

			return this;
		},

		restore: function() {
			this.$element
				.css("position", "absolute")
				.css("top", "0px")
				.css("left", "0px")
				.css("z-index", -1);

			if(this.callback) {
				$(window).off("resize", this.callback);
				this.callback = undefined;
			}
		},

		/*
		 * formatting methods
		 */

		// give the canvas a fixed size, independent of the window size
		fixedSize: function(width, height) {
			this.restore();
			this.resize(width, height);

			return this;
		},

        // TODO: add hideBorder .css class (or check how Boostrap does this)
		// give the canvas a fixed ratio, it will fit into the window
		screenFit: function(ratio) {
			this.restore();

			this.ratio = ratio;

			this.callback = this.fitOnResize.bind(this)
			$(window).resize(this.callback);

			this.callback();

			return this;
		},

		fitOnResize: function() {
			var windowWidth = $(window).width();
			var windowHeight = $(window).height();
			var windowRatio = windowWidth/windowHeight;

			if(this.ratio > windowRatio) {
				// adjust height of the canvas
				var canvasHeight = windowHeight * windowRatio / this.ratio;
				this.resize(windowWidth, canvasHeight);
				this.$element
					.css("top", (windowHeight - canvasHeight) / 2 + "px")
					.css("left", "0px");
			}
			else {
				// adjust width of the canvas
				var canvasWidth = windowWidth * this.ratio / windowRatio;
				this.resize(canvasWidth, windowHeight);
				this.$element
					.css("top", "0px")
					.css("left", (windowWidth - canvasWidth) / 2 + "px");
			}
		},

        // TODO: add hideBorder .css class
		// stretches the canvas to the whole window
		stretch: function() {
			this.restore();

			this.callback = this.stretchOnResize.bind(this);
			$(window).resize(this.callback);

			this.callback();

			return this;
		},

		stretchOnResize: function() {
		    this.resize($(window).width(), $(window).height());
		},

		// request a fullScreen for the canvas
		fullScreen: function() {
			this.restore();

			if (screenfull.enabled) {
			    screenfull.request(this.domElement);
			}

			var rect = this.domElement.getBoundingClientRect();
	        //this.resize(rect.width, rect.height);
			Bloob.log(document.body.clientWidth, document.body.clientHeight);
			Bloob.log(rect.width, rect.height);
			this.$element
				.css("width", "100%")
				.css("height", "100%");
			Bloob.log(this.$element.width(), this.$element.height());
			Bloob.log(this.domElement);
	        this.resize(this.$element.attr("height"), this.$element.attr("height"));

		    return this;
		}
	});

	return Canvas;
});
<|endoftext|>


<commit_before>
// Express - Mocks - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)

/**
 * Mock _request_ object. Optionally pass hash of properties.
 *
 * @param  {hash} request
 * @return {hash}
 * @api public
 */

function mockRequest(request) {
  var mock = {
    listeners: [],
    method: 'GET',
    headers: {},
    uri: {
      path: '/',
      params: {}
    },
    setBodyEncoding : function(type) {
      this.bodyEncoding = type
    },
    addListener : function(event, callback) {
      this.listeners.push({ event : event, callback : callback })
      if (event == 'body') {
        var body = this.body
        this.body = ''
        callback(body)
      }
      else
        callback()
    }
  }
  JSpec.extend(mock, request)
  mock.uri.params = mock.uri.params || {}
  return mock
}

/**
 * Mock _response_ object. Optionally pass hash of properties.
 *
 * @param  {hash} response
 * @return {hash}
 * @api public
 */

function mockResponse(response) {
  var mock = {
    body: null,
    status: 200,
    headers: [],
    sendHeader: function(){},
    sendBody: function(){},
    finish: function(){ this.finished = true }
  }
  JSpec.extend(mock, response)
  return mock
}

/**
 * Mock server callback.
 *
 * @param  {object} request
 * @param  {object} response
 * @api private
 */

orig = Express.server.callback
function mockCallback(request, response) {
  orig(mockRequest(request), mockResponse(response))
}

/**
 * Mock route function for HTTP _method_.
 *
 * @param  {string} method
 * @return {function}
 * @api private
 */

function mockRouteFunctionFor(method) {
  var orig =  Express.routeFunctionFor(method)
  return function(path, options, callback) {
    if ((options && options.constructor == Function) || (callback && callback.constructor == Function)) {
      return orig.apply(Express, arguments)
    }
    var request = mockRequest({ method : method.toUpperCase(), uri : { path : path }})
    if (options)
      JSpec.extend(request, options.request),
      request.uri.path = path
    var response = mockResponse()
    Express.server.callback(request, response)
    return Express.response
  }
}

// --- Module

JSpec.include({
  init : function() {
    Express.server.callback = mockCallback
  },

  utilities : {
    mockRequest : mockRequest,
    mockResponse : mockResponse,
    get  : mockRouteFunctionFor('get'),
    post : mockRouteFunctionFor('post'),
    put  : mockRouteFunctionFor('put'),
    del  : mockRouteFunctionFor('delete')
  }
})
<commit_msg>Misc refactoring to mocks<commit_after>
// Express - Mocks - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)

/**
 * Mock _request_ object. Optionally pass hash of properties.
 *
 * @param  {hash} request
 * @return {hash}
 * @api public
 */

function mockRequest(request) {
  var mock = {
    listeners: [],
    method: 'GET',
    headers: {},
    uri: {
      path: '/',
      params: {}
    },
    setBodyEncoding : function(type) {
      this.bodyEncoding = type
    },
    addListener : function(event, callback) {
      this.listeners.push({ event : event, callback : callback })
      if (event == 'body') {
        var body = this.body
        this.body = ''
        callback(body)
      }
      else
        callback()
    }
  }
  JSpec.extend(mock, request)
  mock.uri.params = mock.uri.params || {}
  return mock
}

/**
 * Mock _response_ object. Optionally pass hash of properties.
 *
 * @param  {hash} response
 * @return {hash}
 * @api public
 */

function mockResponse(response) {
  var mock = {
    body: null,
    status: 200,
    headers: [],
    sendHeader: function(){},
    sendBody: function(){},
    finish: function(){ this.finished = true }
  }
  JSpec.extend(mock, response)
  return mock
}

/**
 * Mock server callback.
 *
 * @param  {object} request
 * @param  {object} response
 * @api private
 */

var orig = Express.server.callback
function mockCallback(request, response) {
  orig(mockRequest(request), mockResponse(response))
}

/**
 * Mock route function for HTTP _method_.
 *
 * @param  {string} method
 * @return {function}
 * @api private
 */

function mockRouteFunctionFor(method) {
  var orig = Express.routeFunctionFor(method)
  return function(path, options, callback) {
    if ((options && options.constructor == Function) || (callback && callback.constructor == Function))
      return orig.apply(Express, arguments)
    var request = mockRequest({ method : method.toUpperCase(), uri : { path : path }})
    if (options)
      JSpec.extend(request, options.request),
      request.uri.path = path
    var response = mockResponse()
    Express.server.callback(request, response)
    return Express.response
  }
}

// --- Module

JSpec.include({
  init : function() {
    Express.server.callback = mockCallback
  },

  utilities : {
    mockRequest : mockRequest,
    mockResponse : mockResponse,
    get  : mockRouteFunctionFor('get'),
    post : mockRouteFunctionFor('post'),
    put  : mockRouteFunctionFor('put'),
    del  : mockRouteFunctionFor('delete')
  }
})
<|endoftext|>


<commit_before>/*
 * stompit.FrameInputStream
 * Copyright (c) 2013 Graham Daws <graham.daws@gmail.com>
 * MIT licensed
 */

var events  = require('events');
var stream  = require('stream');
var util    = require('./util');
var errors  = require('./errors');

var MAX_LINE_LENGTH = 4096;
var CR = '\r'.charCodeAt(0);
var LF = '\n'.charCodeAt(0);

function FrameInputStream(source, options){

    events.EventEmitter.call(this);

    this.TransportError = (options && options.TransportError) || errors.TransportError || Error;
    this.ProtocolError = (options && options.ProtocolError) || errors.ProtocolError || Error;

    if(!this.setVersion('1.1')){
        throw new Error('Could not set to version 1.1 mode');
    }

    this._source = source;

    this._frameQueue = [];

    var self = this;

    // Using nextTick fixes a bug where the stream was set to reading state
    // but the net stream implementor stopped reading. This bus was caused
    // by ignoring recursive readable events triggered by unshifts.
    var parseFrameOnNextTick = process.nextTick.bind(null, this._parseFrame.bind(this));
    source.on('readable', parseFrameOnNextTick);

    var error = processError.bind(this);

    var isEnded = false;
    var end = function(){
        if(!isEnded){
            isEnded = true;
            emptyFrameQueue.apply(self);
            self.emit('end');
        }
    };

    source.on('close', function(hadError){
        if(!hadError){
            end();
        }
    });

    source.on('end', function(){
        if(self._frame && self._frame.readingFrame){
            error(new self.ProtocolError('read frame error'));
        }
        else{
            end();
        }
    });

    source.once('error', function(exception){
        var wrapperException = new self.TransportError(exception.message);
        util.extend(wrapperException, exception);
        error(wrapperException);
    });
}

util.inherits(FrameInputStream, events.EventEmitter);

FrameInputStream.prototype.setVersion = function(version){

    var funcs = supportedVersions[version];

    if(funcs === undefined){
        return false;
    }

    this._version = version;
    this._versionTraits = funcs;

    this._incomingFrameConstructor = function(){
        IncomingFrame.apply(this, arguments);
    };

    util.inherits(this._incomingFrameConstructor, IncomingFrame);

    this._incomingFrameConstructor.prototype._decode = funcs.decode;

    return true;
};

FrameInputStream.prototype.decodeHeaderValue = function(value){
    return version_11.decode(value);
};

FrameInputStream.prototype.getSource = function(){
    return this._source;
};

FrameInputStream.prototype.readFrame = function(callback){

    var IncomingFrameClass = this._incomingFrameConstructor;

    var frame = new IncomingFrameClass(this, callback);

    frame.on('error', function(){});

    if(this._frame === undefined){
        this._frame = frame;
        this._parseFrame();
    }
    else{
        this._frameQueue.push(frame);
    }
};

FrameInputStream.prototype._parseFrame = function(){

    try{
        while(this._frame && this._frame._parse.call(this._frame, this._source));
    }
    catch(exception){

        if(exception instanceof ParseError){
            processError.call(this, exception);
        }
        else{
            throw exception;
        }
    }
};

function emptyFrameQueue(){

    var error = new Error('stream has ended');

    if(this._frame){

        this._frame._invokeCallback(error);

        if(this._frame.readingFrame){
            this._frame.emit('error', error);
        }

        delete this._frame;
    }

    var frameQueue = this._frameQueue;
    var frameQueueLen = frameQueue.length;

    for(var i = 0; i < frameQueueLen; i++){
        frameQueue[i]._invokeCallback(error);
        frameQueue[i].emit('error', error);
    }

    this._frameQueue = [];
}

function processError(exception){
    emptyFrameQueue.apply(this);
    this.emit('error', exception);
}

function ParseError(message){
    this.message = message;
}

ParseError.prototype = Error.prototype;

function IncomingFrame(frameInputStream, callback){

    stream.Readable.call(this);

    this._frameInputStream = frameInputStream;

    this._callback = callback;

    this.readingFrame = false;

    this._lineStash = '';

    this._pushable = true;

    this._parse = readCommandLine;

    this.headers = {};
}

util.inherits(IncomingFrame, stream.Readable);

IncomingFrame.prototype._read = function(){
    if(this._frameInputStream._frame){
        this._frameInputStream._frame._pushable = true;
        this._frameInputStream._parseFrame();
    }
};

IncomingFrame.prototype._readLine = function(source){

    var stashLength = this._lineStash.length;

    var data;

    while((data = source.read()) !== null){

        var dataLength = data.length;

        for(var i = 0; i < dataLength; i++){

            if(stashLength + i + 1 > MAX_LINE_LENGTH){
                throw new ParseError('maximum line length exceeded (' + MAX_LINE_LENGTH + ' character limit)');
            }

            if(data[i] === LF){

                var end = i;

                if(i > 0 && data[i - 1] === CR){
                    end = i - 1;
                }

                var line = this._lineStash + data.toString('utf-8', 0, end);

                this._lineStash = '';

                source.unshift(data.slice(i + 1));

                return line;
            }
        }

        this._lineStash += data.toString('utf-8');
    }

    return null;
};

IncomingFrame.prototype.readEmptyBody = function(callback){

    callback = callback || function(){};

    var self = this;

    var onReadable = function(){
        var buffer = self.read();
        if(buffer !== null){
            callback(false);
            callback = function(){};
        }
    };

    this.once('end', function(){
        self.removeListener('readable', onReadable);
        callback(true);
    });

    this.once('readable', onReadable);

    this.read(0);
};

IncomingFrame.prototype._invokeCallback = function(error){

    var self = this;

    process.nextTick(function(){
        if(self._callback){
            self._callback(error, self);
            delete self._callback;
        }
    });
};

function readCommandLine(source){

    var line = this._readLine(source);

    if(line !== null && line.length > 0){

        this.readingFrame = true;

        this.command = this._decode(line);

        this._parse = readHeaderFieldLine;

        return true;
    }

    return false;
}

function readHeaderFieldLine(source){

    var line = this._readLine(source);

    if(line !== null){

        if(line.length > 0){

            var separator = line.indexOf(':');

            if(separator === -1){
                throw new ParseError('header parse error');
            }

            var header = line.split(':');

            var name = this._decode(line.substring(0, separator));
            var value = this._decode(line.substring(separator + 1));

            if(!this.headers.hasOwnProperty(name)){
                this.headers[name] = value;
            }

            return true;
        }
        else{

            if(this.headers['content-length'] !== undefined){

                var contentLength = parseInt(this.headers['content-length'], 10);

                if(isNaN(contentLength)){
                    throw new ParseError('invalid content-length');
                }

                this.headers['content-length'] = contentLength;

                this._contentLengthRemaining = contentLength;

                this._parse = readFixedLengthBody;
            }
            else{
                this._parse = readBody;
            }

            this._invokeCallback(null);

            // Return false to let the frame user control the reading of the body
        }
    }

    return false;
}

function readBody(source){

    var chunk;

    while(this._pushable && (chunk = source.read()) !== null){

        var terminated = null;

        var chunkLength = chunk.length;
        for(var i = 0; i < chunkLength; i++){
            if(chunk[i] === 0){
                terminated = i;
                break;
            }
        }

        if(terminated === null){
            this._pushable = this.push(chunk);
        }
        else{

            this._parse = readTrailer;

            source.unshift(chunk.slice(terminated + 1));

            // Update parse state before pushing in case of recursive read
            this.push(chunk.slice(0, terminated));
            this.push(null);

            return true;
        }
    }

    return false;
}

function readFixedLengthBody(source){

    var chunk;

    while(this._pushable && (chunk = source.read()) !== null){

        var lengthRemaining = this._contentLengthRemaining;

        if(chunk.length < lengthRemaining){

            this._contentLengthRemaining -= chunk.length;

            this._pushable = this.push(chunk);
        }
        else{

            this._parse = readNullByte;

            source.unshift(chunk.slice(lengthRemaining));

            // Update parse state before pushing in case of recursive read
            this.push(chunk.slice(0, lengthRemaining));

            return true;
        }
    }

    return false;
}

function readNullByte(source){

    var nullByte = source.read(1);

    if(nullByte === null){
        return false;
    }

    if(nullByte[0] !== 0){
        throw new ParseError('expected null byte');
    }

    this._parse = readTrailer;

    this.push(null);

    return true;
}

function readTrailer(source){

    // The peer can drop the connection now without triggering an error
    this.readingFrame = false;

    var chunk;

    while((chunk = source.read(1)) !== null){

        if(chunk[0] == CR){
            this._lineStash = '\r';
            continue;
        }

        if(this._lineStash.length > 0 && chunk[0] !== LF){
            throw new ParseError('invalid EOL sequence');
        }

        this._lineStash = '';

        // If not LF then it's the first character of the next frame
        if(chunk[0] !== LF){
            source.unshift(chunk);
            this._frameInputStream._frame = this._frameInputStream._frameQueue.shift();
            return true;
        }
    }

    return false;
}

function createDecodeFunction(escapeSequences){
    return function(value){
        return value.replace(/\\./gi, function(sequence){
            if(escapeSequences.hasOwnProperty(sequence)){
                return escapeSequences[sequence];
            }
            else{
                throw new ParseError('undefined escape sequence');
            }
        });
    };
}

var version_10 = {
    decode: function(value){return value;}
};

var version_11 = {
    decode: createDecodeFunction({'\\n': '\n', '\\c': ':', '\\\\': '\\'})
};

var version_12 = {
    decode: createDecodeFunction({'\\r': '\r', '\\n': '\n', '\\c': ':', '\\\\': '\\'})
};

var supportedVersions = {
    '1.0': version_10,
    '1.1': version_11,
    '1.2': version_12
};

module.exports = FrameInputStream;
<commit_msg>Don't emit error event for queued frame<commit_after>/*
 * stompit.FrameInputStream
 * Copyright (c) 2013 Graham Daws <graham.daws@gmail.com>
 * MIT licensed
 */

var events  = require('events');
var stream  = require('stream');
var util    = require('./util');
var errors  = require('./errors');

var MAX_LINE_LENGTH = 4096;
var CR = '\r'.charCodeAt(0);
var LF = '\n'.charCodeAt(0);

function FrameInputStream(source, options){

    events.EventEmitter.call(this);

    this.TransportError = (options && options.TransportError) || errors.TransportError || Error;
    this.ProtocolError = (options && options.ProtocolError) || errors.ProtocolError || Error;

    if(!this.setVersion('1.1')){
        throw new Error('Could not set to version 1.1 mode');
    }

    this._source = source;

    this._frameQueue = [];

    var self = this;

    // Using nextTick fixes a bug where the stream was set to reading state
    // but the net stream implementor stopped reading. This bus was caused
    // by ignoring recursive readable events triggered by unshifts.
    var parseFrameOnNextTick = process.nextTick.bind(null, this._parseFrame.bind(this));
    source.on('readable', parseFrameOnNextTick);

    var error = processError.bind(this);

    var isEnded = false;
    var end = function(){
        if(!isEnded){
            isEnded = true;
            emptyFrameQueue.apply(self);
            self.emit('end');
        }
    };

    source.on('close', function(hadError){
        if(!hadError){
            end();
        }
    });

    source.on('end', function(){
        if(self._frame && self._frame.readingFrame){
            error(new self.ProtocolError('read frame error'));
        }
        else{
            end();
        }
    });

    source.once('error', function(exception){
        var wrapperException = new self.TransportError(exception.message);
        util.extend(wrapperException, exception);
        error(wrapperException);
    });
}

util.inherits(FrameInputStream, events.EventEmitter);

FrameInputStream.prototype.setVersion = function(version){

    var funcs = supportedVersions[version];

    if(funcs === undefined){
        return false;
    }

    this._version = version;
    this._versionTraits = funcs;

    this._incomingFrameConstructor = function(){
        IncomingFrame.apply(this, arguments);
    };

    util.inherits(this._incomingFrameConstructor, IncomingFrame);

    this._incomingFrameConstructor.prototype._decode = funcs.decode;

    return true;
};

FrameInputStream.prototype.decodeHeaderValue = function(value){
    return version_11.decode(value);
};

FrameInputStream.prototype.getSource = function(){
    return this._source;
};

FrameInputStream.prototype.readFrame = function(callback){

    var IncomingFrameClass = this._incomingFrameConstructor;

    var frame = new IncomingFrameClass(this, callback);

    frame.on('error', function(){});

    if(this._frame === undefined){
        this._frame = frame;
        this._parseFrame();
    }
    else{
        this._frameQueue.push(frame);
    }
};

FrameInputStream.prototype._parseFrame = function(){

    try{
        while(this._frame && this._frame._parse.call(this._frame, this._source));
    }
    catch(exception){

        if(exception instanceof ParseError){
            processError.call(this, exception);
        }
        else{
            throw exception;
        }
    }
};

function emptyFrameQueue(){

    var error = new Error('stream has ended');

    if(this._frame){

        this._frame._invokeCallback(error);

        if(this._frame.readingFrame){
            this._frame.emit('error', error);
        }

        delete this._frame;
    }

    var frameQueue = this._frameQueue;
    var frameQueueLen = frameQueue.length;

    for(var i = 0; i < frameQueueLen; i++){
        frameQueue[i]._invokeCallback(error);
    }

    this._frameQueue = [];
}

function processError(exception){
    emptyFrameQueue.apply(this);
    this.emit('error', exception);
}

function ParseError(message){
    this.message = message;
}

ParseError.prototype = Error.prototype;

function IncomingFrame(frameInputStream, callback){

    stream.Readable.call(this);

    this._frameInputStream = frameInputStream;

    this._callback = callback;

    this.readingFrame = false;

    this._lineStash = '';

    this._pushable = true;

    this._parse = readCommandLine;

    this.headers = {};
}

util.inherits(IncomingFrame, stream.Readable);

IncomingFrame.prototype._read = function(){
    if(this._frameInputStream._frame){
        this._frameInputStream._frame._pushable = true;
        this._frameInputStream._parseFrame();
    }
};

IncomingFrame.prototype._readLine = function(source){

    var stashLength = this._lineStash.length;

    var data;

    while((data = source.read()) !== null){

        var dataLength = data.length;

        for(var i = 0; i < dataLength; i++){

            if(stashLength + i + 1 > MAX_LINE_LENGTH){
                throw new ParseError('maximum line length exceeded (' + MAX_LINE_LENGTH + ' character limit)');
            }

            if(data[i] === LF){

                var end = i;

                if(i > 0 && data[i - 1] === CR){
                    end = i - 1;
                }

                var line = this._lineStash + data.toString('utf-8', 0, end);

                this._lineStash = '';

                source.unshift(data.slice(i + 1));

                return line;
            }
        }

        this._lineStash += data.toString('utf-8');
    }

    return null;
};

IncomingFrame.prototype.readEmptyBody = function(callback){

    callback = callback || function(){};

    var self = this;

    var onReadable = function(){
        var buffer = self.read();
        if(buffer !== null){
            callback(false);
            callback = function(){};
        }
    };

    this.once('end', function(){
        self.removeListener('readable', onReadable);
        callback(true);
    });

    this.once('readable', onReadable);

    this.read(0);
};

IncomingFrame.prototype._invokeCallback = function(error){

    var self = this;

    process.nextTick(function(){
        if(self._callback){
            self._callback(error, self);
            delete self._callback;
        }
    });
};

function readCommandLine(source){

    var line = this._readLine(source);

    if(line !== null && line.length > 0){

        this.readingFrame = true;

        this.command = this._decode(line);

        this._parse = readHeaderFieldLine;

        return true;
    }

    return false;
}

function readHeaderFieldLine(source){

    var line = this._readLine(source);

    if(line !== null){

        if(line.length > 0){

            var separator = line.indexOf(':');

            if(separator === -1){
                throw new ParseError('header parse error');
            }

            var header = line.split(':');

            var name = this._decode(line.substring(0, separator));
            var value = this._decode(line.substring(separator + 1));

            if(!this.headers.hasOwnProperty(name)){
                this.headers[name] = value;
            }

            return true;
        }
        else{

            if(this.headers['content-length'] !== undefined){

                var contentLength = parseInt(this.headers['content-length'], 10);

                if(isNaN(contentLength)){
                    throw new ParseError('invalid content-length');
                }

                this.headers['content-length'] = contentLength;

                this._contentLengthRemaining = contentLength;

                this._parse = readFixedLengthBody;
            }
            else{
                this._parse = readBody;
            }

            this._invokeCallback(null);

            // Return false to let the frame user control the reading of the body
        }
    }

    return false;
}

function readBody(source){

    var chunk;

    while(this._pushable && (chunk = source.read()) !== null){

        var terminated = null;

        var chunkLength = chunk.length;
        for(var i = 0; i < chunkLength; i++){
            if(chunk[i] === 0){
                terminated = i;
                break;
            }
        }

        if(terminated === null){
            this._pushable = this.push(chunk);
        }
        else{

            this._parse = readTrailer;

            source.unshift(chunk.slice(terminated + 1));

            // Update parse state before pushing in case of recursive read
            this.push(chunk.slice(0, terminated));
            this.push(null);

            return true;
        }
    }

    return false;
}

function readFixedLengthBody(source){

    var chunk;

    while(this._pushable && (chunk = source.read()) !== null){

        var lengthRemaining = this._contentLengthRemaining;

        if(chunk.length < lengthRemaining){

            this._contentLengthRemaining -= chunk.length;

            this._pushable = this.push(chunk);
        }
        else{

            this._parse = readNullByte;

            source.unshift(chunk.slice(lengthRemaining));

            // Update parse state before pushing in case of recursive read
            this.push(chunk.slice(0, lengthRemaining));

            return true;
        }
    }

    return false;
}

function readNullByte(source){

    var nullByte = source.read(1);

    if(nullByte === null){
        return false;
    }

    if(nullByte[0] !== 0){
        throw new ParseError('expected null byte');
    }

    this._parse = readTrailer;

    this.push(null);

    return true;
}

function readTrailer(source){

    // The peer can drop the connection now without triggering an error
    this.readingFrame = false;

    var chunk;

    while((chunk = source.read(1)) !== null){

        if(chunk[0] == CR){
            this._lineStash = '\r';
            continue;
        }

        if(this._lineStash.length > 0 && chunk[0] !== LF){
            throw new ParseError('invalid EOL sequence');
        }

        this._lineStash = '';

        // If not LF then it's the first character of the next frame
        if(chunk[0] !== LF){
            source.unshift(chunk);
            this._frameInputStream._frame = this._frameInputStream._frameQueue.shift();
            return true;
        }
    }

    return false;
}

function createDecodeFunction(escapeSequences){
    return function(value){
        return value.replace(/\\./gi, function(sequence){
            if(escapeSequences.hasOwnProperty(sequence)){
                return escapeSequences[sequence];
            }
            else{
                throw new ParseError('undefined escape sequence');
            }
        });
    };
}

var version_10 = {
    decode: function(value){return value;}
};

var version_11 = {
    decode: createDecodeFunction({'\\n': '\n', '\\c': ':', '\\\\': '\\'})
};

var version_12 = {
    decode: createDecodeFunction({'\\r': '\r', '\\n': '\n', '\\c': ':', '\\\\': '\\'})
};

var supportedVersions = {
    '1.0': version_10,
    '1.1': version_11,
    '1.2': version_12
};

module.exports = FrameInputStream;
<|endoftext|>