<commit_before>var fs = require('fs');
var url = require('url');
var path = require('path');
var http = require('http');
var st = require('st');
var mkdirp = require('mkdirp');
var response = require('response');
var socketio = require('socket.io');
var nodemailer = require('nodemailer');
var sgTransport = require('nodemailer-sendgrid-transport');
var filter = require('filter-object');
var extend = require('extend');
var dotenv = require('dotenv');
var cors = require('corsify');

var Handlebars = require('handlebars');
require('handlebars-layouts')(Handlebars);
require('./handlebars-helpers')(Handlebars);

var initdb = require('./db');

module.exports = Server;

/*
* Main server constructor function
*/

function Server (opts, cb) {
  if (!(this instanceof Server)) return new Server(opts, cb);

  opts = extend({
    site: {
      title: 'flatsheet',
      email: 'hi@flatsheet.io',
      url: 'http://127.0.0.1',
      contact: 'flatsheet admin'
    }
  }, opts)

  var self = this;
  this.site = opts.site;

  var envFilePath = path.join((opts.dir || process.cwd()), '.env');

  if (fs.existsSync(envFilePath)) {
    var envFile = fs.readFileSync(envFilePath);
    var secrets = dotenv.parse(envFile);
  } else {
    var secrets = {
      SENDGRID_USER: process.env.SENDGRID_USER,
      SENDGRID_PASS: process.env.SENDGRID_PASS
    }
  }

  /*
  * Set path for static files
  */

  this.staticFiles = opts.staticFiles || path.join(__dirname, '..', 'public');
  this.staticFileUrl = opts.staticFileUrl || '/public/'

  mkdirp(this.staticFiles, function (err) {
    if (err) console.error(err)
  });

  /*
  * Set path for db
  */

  this.dataDir = opts.dataDir || opts.db || path.join(__dirname, '..', 'data');

  mkdirp(this.dataDir, function (err) {
    if (err) console.error(err);
    else initdb(self, cb);
  });

  /*
  * Email
  */

  var options = {
    auth: {
      api_user: secrets.SENDGRID_USER,
      api_key: secrets.SENDGRID_PASS
    }
  };

  this.email = nodemailer.createTransport(sgTransport(options));

  /*
  * Set up the application's views
  */

  this.views = {}
  this.viewsDir = path.join(__dirname, '/../views/');
  this.viewData = { site: opts.site };
  this.addViews();

  if (opts.views) {
    if (opts.views[opts.views.length - 1] !== '/')  opts.views += '/';
    this.viewsOverrideDir = opts.views;
    this.overrideViews();
  }

}

/*
*  Get an account based on the session
*/
Server.prototype.getAccountBySession = function (req, cb) {
  var self = this;

  this.auth.getSession(req, function (sessionError, session) {
    if (sessionError) return cb(sessionError);

    self.accountdown.get(session.data.username, function (accountError, account) {
      if (accountError) return cb(accountError);
      if (!account) return cb(null, { active: false }, session);

      var accountFiltered = filter(account, '!email');
      accountFiltered.active = true;
      return cb(null, accountFiltered, session);
    });
  });
}

/*
*  listen method for starting the server
*/
Server.prototype.listen = function (port, cb) {
  if (typeof port === 'function') {
    cb = port;
    port = null;
  }

  this.port = port || (process.env.NODE_ENV === 'production' ? 80 : 3333);
  var self = this;

  var cb = cb || function () {
    console.log('flatsheet server listening at ' + self.site.url);
  }

  var staticFiles = st({ path: this.staticFiles, url: this.staticFileUrl });
  var landing = require('../routes/landing')(this);
  var sessions = require('../routes/sessions')(this);
  var accounts = require('../routes/accounts')(this);
  var accountsAPIv20 = require('../routes/accounts-api-v2-0')(this);
  var sheets = require('../routes/sheets')(this);
  var sheetsAPIv20 = require('../routes/sheets-api-v2-0')(this);

  function handler (req, res) {
    if (staticFiles(req, res)) return;
    if (landing.match(req, res)) return;
    if (sessions.match(req, res)) return;
    if (accounts.match(req, res)) return;
    if (accountsAPIv20.match(req, res)) return;
    if (sheets.match(req, res)) return;
    if (sheetsAPIv20.match(req, res)) return;

    else {
      self.getAccountBySession(req, function (err, account, session) {
        response().html(self.render('404', { account: account })).pipe(res);
      });
    }
  }

  var opts = {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
    'Access-Control-Allow-Headers': 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept, Authorization'
  };

  this._server = http.createServer(cors(opts, handler));
  var io = socketio(this._server);
  require('./realtime-editing')(io, this);
  this._server.listen(this.port, cb);
};

/*
*  listen method for stopping the server
*/
Server.prototype.close = function () {
  this._server.close();
  this.db.close();
};

/*
* Create views on application startup
*/
Server.prototype.addViews = function () {
  var self = this;

  fs.readdir(this.viewsDir, function (err, files) {
    files.forEach(function (file) {
      if (file === 'layout.html') {
        var filepath = self.viewsDir + 'layout.html';
        Handlebars.registerPartial('layout', fs.readFileSync(filepath, 'utf8'));
      }
      else self.addView(file);
    });
  });
}

/*
* Add a view to the list of views
*/
Server.prototype.addView = function (file, viewsDir) {
  var dir = viewsDir || this.viewsDir;
  return this.views[file.split('.')[0]] = this.compileView(dir + file);
}

/*
* compile a view
*/
Server.prototype.compileView = function (filepath) {
  var template = Handlebars.compile(fs.readFileSync(filepath, 'utf8'));
  return template;
}

/*
* Override views
*/
Server.prototype.overrideViews = function () {
  var self = this;

  fs.readdir(this.viewsOverrideDir, function (err, files) {
    files.forEach(function (file) {
      if (file === 'layout.html') {
        var filepath = self.viewsOverrideDir + 'layout.html';
        Handlebars.registerPartial('layout', fs.readFileSync(filepath, 'utf8'));
      }
      else self.addView(file, self.viewsOverrideDir);
    });
  });
};

/*
* Method for rendering html views
*/
Server.prototype.render = function (view, data) {
  var data = extend(data || {}, this.viewData);
  return this.views[view](data);
};
<commit_msg>Remove unecessary else<commit_after>var fs = require('fs');
var url = require('url');
var path = require('path');
var http = require('http');
var st = require('st');
var mkdirp = require('mkdirp');
var response = require('response');
var socketio = require('socket.io');
var nodemailer = require('nodemailer');
var sgTransport = require('nodemailer-sendgrid-transport');
var filter = require('filter-object');
var extend = require('extend');
var dotenv = require('dotenv');
var cors = require('corsify');

var Handlebars = require('handlebars');
require('handlebars-layouts')(Handlebars);
require('./handlebars-helpers')(Handlebars);

var initdb = require('./db');

module.exports = Server;

/*
* Main server constructor function
*/

function Server (opts, cb) {
  if (!(this instanceof Server)) return new Server(opts, cb);

  opts = extend({
    site: {
      title: 'flatsheet',
      email: 'hi@flatsheet.io',
      url: 'http://127.0.0.1',
      contact: 'flatsheet admin'
    }
  }, opts)

  var self = this;
  this.site = opts.site;

  var envFilePath = path.join((opts.dir || process.cwd()), '.env');

  if (fs.existsSync(envFilePath)) {
    var envFile = fs.readFileSync(envFilePath);
    var secrets = dotenv.parse(envFile);
  } else {
    var secrets = {
      SENDGRID_USER: process.env.SENDGRID_USER,
      SENDGRID_PASS: process.env.SENDGRID_PASS
    }
  }

  /*
  * Set path for static files
  */

  this.staticFiles = opts.staticFiles || path.join(__dirname, '..', 'public');
  this.staticFileUrl = opts.staticFileUrl || '/public/'

  mkdirp(this.staticFiles, function (err) {
    if (err) console.error(err)
  });

  /*
  * Set path for db
  */

  this.dataDir = opts.dataDir || opts.db || path.join(__dirname, '..', 'data');

  mkdirp(this.dataDir, function (err) {
    if (err) console.error(err);
    else initdb(self, cb);
  });

  /*
  * Email
  */

  var options = {
    auth: {
      api_user: secrets.SENDGRID_USER,
      api_key: secrets.SENDGRID_PASS
    }
  };

  this.email = nodemailer.createTransport(sgTransport(options));

  /*
  * Set up the application's views
  */

  this.views = {}
  this.viewsDir = path.join(__dirname, '/../views/');
  this.viewData = { site: opts.site };
  this.addViews();

  if (opts.views) {
    if (opts.views[opts.views.length - 1] !== '/')  opts.views += '/';
    this.viewsOverrideDir = opts.views;
    this.overrideViews();
  }

}

/*
*  Get an account based on the session
*/
Server.prototype.getAccountBySession = function (req, cb) {
  var self = this;

  this.auth.getSession(req, function (sessionError, session) {
    if (sessionError) return cb(sessionError);

    self.accountdown.get(session.data.username, function (accountError, account) {
      if (accountError) return cb(accountError);
      if (!account) return cb(null, { active: false }, session);

      var accountFiltered = filter(account, '!email');
      accountFiltered.active = true;
      return cb(null, accountFiltered, session);
    });
  });
}

/*
*  listen method for starting the server
*/
Server.prototype.listen = function (port, cb) {
  if (typeof port === 'function') {
    cb = port;
    port = null;
  }

  this.port = port || (process.env.NODE_ENV === 'production' ? 80 : 3333);
  var self = this;

  var cb = cb || function () {
    console.log('flatsheet server listening at ' + self.site.url);
  }

  var staticFiles = st({ path: this.staticFiles, url: this.staticFileUrl });
  var landing = require('../routes/landing')(this);
  var sessions = require('../routes/sessions')(this);
  var accounts = require('../routes/accounts')(this);
  var accountsAPIv20 = require('../routes/accounts-api-v2-0')(this);
  var sheets = require('../routes/sheets')(this);
  var sheetsAPIv20 = require('../routes/sheets-api-v2-0')(this);

  function handler (req, res) {
    if (staticFiles(req, res)) return;
    if (landing.match(req, res)) return;
    if (sessions.match(req, res)) return;
    if (accounts.match(req, res)) return;
    if (accountsAPIv20.match(req, res)) return;
    if (sheets.match(req, res)) return;
    if (sheetsAPIv20.match(req, res)) return;

    self.getAccountBySession(req, function (err, account, session) {
      response().html(self.render('404', { account: account })).pipe(res);
    });
  }

  var opts = {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
    'Access-Control-Allow-Headers': 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept, Authorization'
  };

  this._server = http.createServer(cors(opts, handler));
  var io = socketio(this._server);
  require('./realtime-editing')(io, this);
  this._server.listen(this.port, cb);
};

/*
*  listen method for stopping the server
*/
Server.prototype.close = function () {
  this._server.close();
  this.db.close();
};

/*
* Create views on application startup
*/
Server.prototype.addViews = function () {
  var self = this;

  fs.readdir(this.viewsDir, function (err, files) {
    files.forEach(function (file) {
      if (file === 'layout.html') {
        var filepath = self.viewsDir + 'layout.html';
        Handlebars.registerPartial('layout', fs.readFileSync(filepath, 'utf8'));
      }
      else self.addView(file);
    });
  });
}

/*
* Add a view to the list of views
*/
Server.prototype.addView = function (file, viewsDir) {
  var dir = viewsDir || this.viewsDir;
  return this.views[file.split('.')[0]] = this.compileView(dir + file);
}

/*
* compile a view
*/
Server.prototype.compileView = function (filepath) {
  var template = Handlebars.compile(fs.readFileSync(filepath, 'utf8'));
  return template;
}

/*
* Override views
*/
Server.prototype.overrideViews = function () {
  var self = this;

  fs.readdir(this.viewsOverrideDir, function (err, files) {
    files.forEach(function (file) {
      if (file === 'layout.html') {
        var filepath = self.viewsOverrideDir + 'layout.html';
        Handlebars.registerPartial('layout', fs.readFileSync(filepath, 'utf8'));
      }
      else self.addView(file, self.viewsOverrideDir);
    });
  });
};

/*
* Method for rendering html views
*/
Server.prototype.render = function (view, data) {
  var data = extend(data || {}, this.viewData);
  return this.views[view](data);
};
<|endoftext|>


<commit_before>import Collection from 'ampersand-rest-collection'
import Repo from './repo'

export default Collection.extend({
  model: Repo,

  initialize () {
    app.firebase.child('repos').on('value', (snapshot) => {
      let val = snapshot.val()
      if (!Array.isArray(val)) {
        val = [val]
      }
      let repos = _.map(val[0], function(value, key) {
        value.id = key
        return value
      })
      this.add(repos)
    })
  },
  destroy (repo) {
    app.firebase.child('repos').child(repo.id).set({})
    this.remove(repo)
  },
  create (name) {
    let repo = {name: name, data: {nodes: [], edges: []}}
    app.firebase.child('repos').push(repo)
    let model = this.add(repo)
    return model.appUrl
  },

  getByName (name) {
    let model = this.findWhere({name: name})
    return model
  }
})
<commit_msg>Saving working again<commit_after>import Collection from 'ampersand-rest-collection'
import Repo from './repo'

export default Collection.extend({
  model: Repo,

  initialize () {
    app.firebase.child('repos').on('value', (snapshot) => {
      let val = snapshot.val()
      if (!Array.isArray(val)) {
        val = [val]
      }
      let repos = _.map(val[0], function(value, key) {
        value.id = key
        return value
      })
      this.add(repos)
    })
  },
  destroy (repo) {
    app.firebase.child('repos').child(repo.id).set({})
    this.remove(repo)
  },
  create (name) {
    let repo = {name: name, data: {nodes: [], edges: []}}
    app.firebase.child('repos').push(repo)
    return ('/repo/' + name)
  },

  getByName (name) {
    let model = this.findWhere({name: name})
    return model
  }
})
<|endoftext|>


<commit_before>'use strict';

const fs = require('graceful-fs');
const path = require('path');
const tsort = require('tsort');
const createAliasResolver = require('./aliases');
const UA = require('./UA');
const sourceslib = require('./sources');
const appVersion = require('../package.json').version;
const Handlebars = require('handlebars');
const cloneDeep = require('lodash').cloneDeep;
const streamFromPromise = require('stream-from-promise');
const lazystream = require('lazystream');
const streamFromString = require('from2-string');
const mergeStream = require('merge2');
const streamToString = require('stream-to-string');

const rumTemplate = Handlebars.compile(fs.readFileSync(path.join(__dirname, 'rumTemplate.js.handlebars'), 'utf-8'));

function listAllPolyfills() {
	return sourceslib.listPolyfills();
}

function describePolyfill(featureName) {
	return Promise.resolve(sourceslib.getPolyfillMetaSync(featureName));
}

function getOptions(opts) {
	opts = Object.assign({
		uaString: '',
		minify: true,
		unknown: 'ignore',
		features: {},
		excludes: [],
		rum: false
	}, opts);

	// Normalise flags
	Object.keys(opts.features).forEach(k => {
		if (!opts.features[k].flags) {
			opts.features[k].flags = new Set();
		} else if (opts.features[k].flags.constructor !== Set) {
			opts.features[k].flags = new Set(opts.features[k].flags);
		}
	});
	return opts;
}

/**
 * Given a set of features that should be polyfilled in 'options.features' (with flags i.e. `{<featurename>: {flags:Set[<flaglist>]}, ...}`), determine which have a configuration valid for the given options.uaString, and return a promise of set of canonical (unaliased) features (with flags) and polyfills.
 *
 * @param {object} options - Valid keys are uaString, minify, unknown, excludes, rum and features
 * @return {promise} - Canonicalised feature definitions filtered for UA
 */
function getPolyfills(options) {

	options = getOptions(options);
	const ua = new UA(options.uaString);

	const resolveAliases = createAliasResolver(
		function aliasFromConfig(featureName) {
			return sourceslib.getConfigAliasesSync(featureName);
		},
		function aliasAll(featureName) {
			return (featureName === 'all') ? sourceslib.listPolyfillsSync() : undefined;
		}
	);

	const resolveDependencies = createAliasResolver(
		function aliasDependencies(featureName) {
			const meta = sourceslib.getPolyfillMetaSync(featureName);
			return (meta && meta.dependencies || [])
				.filter(depName => options.excludes.indexOf(depName) === -1)
				.concat(featureName)
			;
		}
	);

	// Filter the features object to remove features not suitable for the current UA
	const filterForUATargeting = function(features) {
		const featuresList = Object.keys(features);
		return featuresList.map(featureName => {
			const meta = sourceslib.getPolyfillMetaSync(featureName);
			if (!meta) return false;

			const isBrowserMatch = (meta.browsers && meta.browsers[ua.getFamily()] && ua.satisfies(meta.browsers[ua.getFamily()]));
			const hasAlwaysFlagOverride = (features[featureName].flags.has('always'));
			const unknownOverride = (options.unknown === 'polyfill' && ua.isUnknown());

			return (isBrowserMatch || hasAlwaysFlagOverride || unknownOverride) ? featureName : false;
		}).reduce(function(out, key) {
			if (key) out[key] = features[key];
			return out;
		}, {});
	};

	const filterForExcludes = function(features) {
		Object.keys(features).forEach(featureName => {
			if (options.excludes.indexOf(featureName) !== -1) {
				delete features[featureName];
			}
		});
		return features;
	};

	return Promise.resolve(options.features)
		.then(resolveAliases)
		.then(filterForUATargeting)
		.then(resolveDependencies)
		.then(filterForUATargeting)
		.then(filterForExcludes)
	;
}

function getPolyfillString(options) {

	options = getOptions(options);
	const ua = new UA(options.uaString);
	const uaDebugName = ua.getFamily() + '/' + ua.getVersion() + ((ua.isUnknown() || !ua.meetsBaseline()) ? ' (unknown/unsupported; using policy `unknown='+options.unknown+'`)' : '');
	const lf = options.minify ? '' : '\n';
	const allWarnText = 'Using the `all` alias with polyfill.io is a very bad idea. In a future version of the service, `all` will deliver the same behaviour as `default`, so we recommend using `default` instead.';
	const output = mergeStream();
	const explainerComment = [];
	// Check UA and turn requested features into a list of polyfills
	const unsupportedUA = ((!ua.meetsBaseline() || ua.isUnknown()) && options.unknown !== 'polyfill');

	Promise.resolve(unsupportedUA ? {} : getPolyfills(options))

	// Build a polyfill bundle of polyfill sources sorted in dependency order
	.then(targetedFeatures => {
		const warnings = {unknown:[]};
		const graph = tsort();

		Object.keys(targetedFeatures).forEach(featureName => {
			const feature = targetedFeatures[featureName];
			const polyfill = sourceslib.getPolyfillMetaSync(featureName);
			if (!polyfill) {
				warnings.unknown.push(featureName);
			} else {

				graph.add(featureName);

				if (polyfill.dependencies) {
					polyfill.dependencies.forEach(depName => {
						if (depName in targetedFeatures) {
							graph.add(depName, featureName);
						}
					});
				}

				feature.comment = featureName + ', License: ' + (polyfill.license || 'CC0') + ((feature.aliasOf && feature.aliasOf.size) ? ' (required by "' + Array.from(feature.aliasOf).join('", "') + '")' : '');
			}
		});

		const sortedFeatures = graph.sort();

		if (!options.minify) {
			explainerComment.push(
				'Polyfill service ' + ((process.env.NODE_ENV === 'production') ? 'v'+appVersion : 'DEVELOPMENT MODE - for live use set NODE_ENV to \'production\''),
				'For detailed credits and licence information see https://github.com/financial-times/polyfill-service.',
				'',
				'UA detected: ' + uaDebugName,
				'Features requested: ' + Object.keys(options.features),
				''
			);
			if (!ua.meetsBaseline() && ua.getBaseline()) {
				explainerComment.push(
					'Version range for polyfill support in ' + ua.getFamily() + ' is: ' + ua.getBaseline(),
					''
				);
			}
			explainerComment.push(...sortedFeatures
				.filter(featureName => (targetedFeatures[featureName] && targetedFeatures[featureName].comment))
				.map(featureName => '- ' + targetedFeatures[featureName].comment)
			);
			if (warnings.unknown.length) {
				explainerComment.push(
					'',
					'These features were not recognised:',
					warnings.unknown.map(s => '- '+s)
				);
			}
			if ('all' in options.features) {
				explainerComment.push('', allWarnText);
			}
		} else {
			explainerComment.push('Disable minification (remove `.min` from URL path) for more info');
		}
		output.add(streamFromString('/* ' + explainerComment.join('\n * ') + ' */\n\n'));

		if (!sortedFeatures.length) {
			if (!options.minify) {
				output.add(streamFromString('/* No polyfills found for current settings */\n'));
			}
			return;
		}

		// Outer closure hides private features from global scope
		output.add(streamFromString("(function(undefined) {" + lf));

		if (options.rum && process.env.RUM_BEACON_HOST) {
			output.add(new lazystream.Readable(() => {
				const optionsForRUM = cloneDeep(options);
				Object.keys(options.features).forEach(f => optionsForRUM.features[f].flags.add('always'));

				return streamFromPromise(getPolyfills(optionsForRUM)
					.then(rumFeatures => {
						return rumTemplate({
							host: process.env.RUM_BEACON_HOST,
							features: Object.keys(rumFeatures).reduce((op, featureName) => {
								const polyfill = sourceslib.getPolyfillMetaSync(featureName);
								if (polyfill.detectSource) {
									op.push({name: featureName, detect: polyfill.detectSource});
								}
								return op;
							}, [])
						});
					}));
			}));
		}

		// Using the graph, stream all the polyfill sources in dependency order
		for (const featureName of sortedFeatures) {
			const polyfill = sourceslib.getPolyfillMetaSync(featureName);
			const wrapInDetect = (targetedFeatures[featureName].flags.has('gated') && polyfill.detectSource);
			if (wrapInDetect) {
				output.add(streamFromString("if (!(" + polyfill.detectSource + ")) {" + lf));
			}

			output.add(sourceslib.streamPolyfillSource(featureName, (options.minify ? 'min' : 'raw')));

			if (wrapInDetect) {
				output.add(streamFromString(lf + "}" + lf + lf));
			}
		}
		// Invoke the closure, binding `this` to window (in a browser),
		// self (in a web worker), or global (in Node/IOjs)
		output.add(streamFromString("})" + lf + ".call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});" + lf));

		if ('all' in options.features) {
			output.add(streamFromString("\nconsole.log('"+allWarnText+"');\n"));
		}
	});

	return options.stream ? output : Promise.resolve(streamToString(output));
}

module.exports = {
	describePolyfill: describePolyfill,
	listAllPolyfills: listAllPolyfills,
	getPolyfills: getPolyfills,
	getPolyfillString: getPolyfillString,
	normalizeUserAgent: UA.normalize,
};
<commit_msg>Fix script injection in 'unknown' parameter inside library (#1224)<commit_after>'use strict';

const fs = require('graceful-fs');
const path = require('path');
const tsort = require('tsort');
const createAliasResolver = require('./aliases');
const UA = require('./UA');
const sourceslib = require('./sources');
const appVersion = require('../package.json').version;
const Handlebars = require('handlebars');
const cloneDeep = require('lodash').cloneDeep;
const streamFromPromise = require('stream-from-promise');
const lazystream = require('lazystream');
const streamFromString = require('from2-string');
const mergeStream = require('merge2');
const streamToString = require('stream-to-string');

const rumTemplate = Handlebars.compile(fs.readFileSync(path.join(__dirname, 'rumTemplate.js.handlebars'), 'utf-8'));

function listAllPolyfills() {
	return sourceslib.listPolyfills();
}

function describePolyfill(featureName) {
	return Promise.resolve(sourceslib.getPolyfillMetaSync(featureName));
}

function getOptions(opts) {
	opts = Object.assign({
		uaString: '',
		minify: true,
		unknown: 'ignore',
		features: {},
		excludes: [],
		rum: false
	}, opts);

	if (opts.unknown !== 'polyfill') {
		opts.unknown = 'ignore';
	}

	// Normalise flags
	Object.keys(opts.features).forEach(k => {
		if (!opts.features[k].flags) {
			opts.features[k].flags = new Set();
		} else if (opts.features[k].flags.constructor !== Set) {
			opts.features[k].flags = new Set(opts.features[k].flags);
		}
	});
	return opts;
}

/**
 * Given a set of features that should be polyfilled in 'options.features' (with flags i.e. `{<featurename>: {flags:Set[<flaglist>]}, ...}`), determine which have a configuration valid for the given options.uaString, and return a promise of set of canonical (unaliased) features (with flags) and polyfills.
 *
 * @param {object} options - Valid keys are uaString, minify, unknown, excludes, rum and features
 * @return {promise} - Canonicalised feature definitions filtered for UA
 */
function getPolyfills(options) {

	options = getOptions(options);
	const ua = new UA(options.uaString);

	const resolveAliases = createAliasResolver(
		function aliasFromConfig(featureName) {
			return sourceslib.getConfigAliasesSync(featureName);
		},
		function aliasAll(featureName) {
			return (featureName === 'all') ? sourceslib.listPolyfillsSync() : undefined;
		}
	);

	const resolveDependencies = createAliasResolver(
		function aliasDependencies(featureName) {
			const meta = sourceslib.getPolyfillMetaSync(featureName);
			return (meta && meta.dependencies || [])
				.filter(depName => options.excludes.indexOf(depName) === -1)
				.concat(featureName)
			;
		}
	);

	// Filter the features object to remove features not suitable for the current UA
	const filterForUATargeting = function(features) {
		const featuresList = Object.keys(features);
		return featuresList.map(featureName => {
			const meta = sourceslib.getPolyfillMetaSync(featureName);
			if (!meta) return false;

			const isBrowserMatch = (meta.browsers && meta.browsers[ua.getFamily()] && ua.satisfies(meta.browsers[ua.getFamily()]));
			const hasAlwaysFlagOverride = (features[featureName].flags.has('always'));
			const unknownOverride = (options.unknown === 'polyfill' && ua.isUnknown());

			return (isBrowserMatch || hasAlwaysFlagOverride || unknownOverride) ? featureName : false;
		}).reduce(function(out, key) {
			if (key) out[key] = features[key];
			return out;
		}, {});
	};

	const filterForExcludes = function(features) {
		Object.keys(features).forEach(featureName => {
			if (options.excludes.indexOf(featureName) !== -1) {
				delete features[featureName];
			}
		});
		return features;
	};

	return Promise.resolve(options.features)
		.then(resolveAliases)
		.then(filterForUATargeting)
		.then(resolveDependencies)
		.then(filterForUATargeting)
		.then(filterForExcludes)
	;
}

function getPolyfillString(options) {

	options = getOptions(options);
	const ua = new UA(options.uaString);
	const uaDebugName = ua.getFamily() + '/' + ua.getVersion() + ((ua.isUnknown() || !ua.meetsBaseline()) ? ' (unknown/unsupported; using policy `unknown='+options.unknown+'`)' : '');
	const lf = options.minify ? '' : '\n';
	const allWarnText = 'Using the `all` alias with polyfill.io is a very bad idea. In a future version of the service, `all` will deliver the same behaviour as `default`, so we recommend using `default` instead.';
	const output = mergeStream();
	const explainerComment = [];
	// Check UA and turn requested features into a list of polyfills
	const unsupportedUA = ((!ua.meetsBaseline() || ua.isUnknown()) && options.unknown !== 'polyfill');

	Promise.resolve(unsupportedUA ? {} : getPolyfills(options))

	// Build a polyfill bundle of polyfill sources sorted in dependency order
	.then(targetedFeatures => {
		const warnings = {unknown:[]};
		const graph = tsort();

		Object.keys(targetedFeatures).forEach(featureName => {
			const feature = targetedFeatures[featureName];
			const polyfill = sourceslib.getPolyfillMetaSync(featureName);
			if (!polyfill) {
				warnings.unknown.push(featureName);
			} else {

				graph.add(featureName);

				if (polyfill.dependencies) {
					polyfill.dependencies.forEach(depName => {
						if (depName in targetedFeatures) {
							graph.add(depName, featureName);
						}
					});
				}

				feature.comment = featureName + ', License: ' + (polyfill.license || 'CC0') + ((feature.aliasOf && feature.aliasOf.size) ? ' (required by "' + Array.from(feature.aliasOf).join('", "') + '")' : '');
			}
		});

		const sortedFeatures = graph.sort();

		if (!options.minify) {
			explainerComment.push(
				'Polyfill service ' + ((process.env.NODE_ENV === 'production') ? 'v'+appVersion : 'DEVELOPMENT MODE - for live use set NODE_ENV to \'production\''),
				'For detailed credits and licence information see https://github.com/financial-times/polyfill-service.',
				'',
				'UA detected: ' + uaDebugName,
				'Features requested: ' + Object.keys(options.features),
				''
			);
			if (!ua.meetsBaseline() && ua.getBaseline()) {
				explainerComment.push(
					'Version range for polyfill support in ' + ua.getFamily() + ' is: ' + ua.getBaseline(),
					''
				);
			}
			explainerComment.push(...sortedFeatures
				.filter(featureName => (targetedFeatures[featureName] && targetedFeatures[featureName].comment))
				.map(featureName => '- ' + targetedFeatures[featureName].comment)
			);
			if (warnings.unknown.length) {
				explainerComment.push(
					'',
					'These features were not recognised:',
					warnings.unknown.map(s => '- '+s)
				);
			}
			if ('all' in options.features) {
				explainerComment.push('', allWarnText);
			}
		} else {
			explainerComment.push('Disable minification (remove `.min` from URL path) for more info');
		}
		output.add(streamFromString('/* ' + explainerComment.join('\n * ') + ' */\n\n'));

		if (!sortedFeatures.length) {
			if (!options.minify) {
				output.add(streamFromString('/* No polyfills found for current settings */\n'));
			}
			return;
		}

		// Outer closure hides private features from global scope
		output.add(streamFromString("(function(undefined) {" + lf));

		if (options.rum && process.env.RUM_BEACON_HOST) {
			output.add(new lazystream.Readable(() => {
				const optionsForRUM = cloneDeep(options);
				Object.keys(options.features).forEach(f => optionsForRUM.features[f].flags.add('always'));

				return streamFromPromise(getPolyfills(optionsForRUM)
					.then(rumFeatures => {
						return rumTemplate({
							host: process.env.RUM_BEACON_HOST,
							features: Object.keys(rumFeatures).reduce((op, featureName) => {
								const polyfill = sourceslib.getPolyfillMetaSync(featureName);
								if (polyfill.detectSource) {
									op.push({name: featureName, detect: polyfill.detectSource});
								}
								return op;
							}, [])
						});
					}));
			}));
		}

		// Using the graph, stream all the polyfill sources in dependency order
		for (const featureName of sortedFeatures) {
			const polyfill = sourceslib.getPolyfillMetaSync(featureName);
			const wrapInDetect = (targetedFeatures[featureName].flags.has('gated') && polyfill.detectSource);
			if (wrapInDetect) {
				output.add(streamFromString("if (!(" + polyfill.detectSource + ")) {" + lf));
			}

			output.add(sourceslib.streamPolyfillSource(featureName, (options.minify ? 'min' : 'raw')));

			if (wrapInDetect) {
				output.add(streamFromString(lf + "}" + lf + lf));
			}
		}
		// Invoke the closure, binding `this` to window (in a browser),
		// self (in a web worker), or global (in Node/IOjs)
		output.add(streamFromString("})" + lf + ".call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});" + lf));

		if ('all' in options.features) {
			output.add(streamFromString("\nconsole.log('"+allWarnText+"');\n"));
		}
	});

	return options.stream ? output : Promise.resolve(streamToString(output));
}

module.exports = {
	describePolyfill: describePolyfill,
	listAllPolyfills: listAllPolyfills,
	getPolyfills: getPolyfills,
	getPolyfillString: getPolyfillString,
	normalizeUserAgent: UA.normalize,
};
<|endoftext|>


<commit_before>var express = require('express');
var url = require('url');
var net = require('net');
var path = require('path');
var tls = require('tls');
var fs = require('fs');
var socks = require('socksv5');
var EventEmitter = require('events').EventEmitter;
var extend = require('util')._extend;
var util = require('./util');
var logger = require('./util/logger');
var rules = require('./rules');
var dispatch = require('./https');
var httpsUtil = require('./https/util');
var rulesUtil = require('./rules/util');
var initDataServer = require('./util/data-server');
var initLogServer = require('./util/log-server');
var pluginMgr = require('./plugins');
var config = require('./config');
var loadService = require('./service');
var LOCALHOST = '127.0.0.1';
var TUNNEL_HOST_RE = /^[^:\/]+\.[^:\/]+:\d+$/;
var STATUS_CODES = require('http').STATUS_CODES || {};
var index = 0;

function tunnelProxy(server, proxy) {
//see: https://github.com/joyent/node/issues/9272
  if (typeof tls.checkServerIdentity == 'function') {
    var checkServerIdentity = tls.checkServerIdentity;
    tls.checkServerIdentity = function() {
      try {
        return checkServerIdentity.apply(this, arguments);
      } catch(err) {
        logger.error(err);
        return err;
      }
    };
  }

  process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
  server.on('connect', function(req, reqSocket, head) {//ws, wss, https proxy
    var tunnelUrl = req.fullUrl = util.setProtocol(TUNNEL_HOST_RE.test(req.url) ? req.url : req.headers.host, true);
    var options;
    var parseUrl = function (_url, port) {
      _url = _url || tunnelUrl;
      options = req.options = url.parse(_url);
      options.port = options.port || port || 443;
      return options;
    };
    parseUrl();
    tunnelUrl = req.fullUrl = 'tunnel://' + options.host;
    var resSocket, proxySocket, responsed, reqEmitter, data, rulesMgr;
    req.isTunnel = true;
    req.clientIp = util.getClientIp(req) || LOCALHOST;
    req.reqId = ++index;
    var hostname = options.hostname;
    var policy = req.headers[config.WHISTLE_POLICY_HEADER];
    var useTunnelPolicy = policy == 'tunnel';
    var isLocalUIUrl = !useTunnelPolicy && (config.isLocalUIUrl(hostname) || config.isPluginUrl(hostname));
    var _rules = req.rules = isLocalUIUrl ? {} : rules.resolveRules(tunnelUrl);
    rules.resolveFileRules(req, function() {
      var plugin = pluginMgr.resolveWhistlePlugins(req);
      pluginMgr.getTunnelRules(req, function(_rulesMgr) {
        if (_rulesMgr) {
          rulesMgr = _rulesMgr;
          util.mergeRules(req, rulesMgr.resolveRules(tunnelUrl));
          plugin = pluginMgr.getPluginByRuleUrl(util.rule.getUrl(_rules.rule));
        }
        var filter = req.filter;
        var disable = req.disable;
        abortIfUnavailable(reqSocket);
        if (req.headers[config.WEBUI_HEAD]) {
          return reqSocket.destroy();
        }
        if (!useTunnelPolicy && (policy === 'intercept' || isLocalUIUrl || ((filter.https || filter.intercept
            || rulesUtil.properties.get('interceptHttpsConnects')) && !disable.intercept && !disable.https))) {
          dispatch(reqSocket, hostname, proxy, function(_resSocket) {
            resSocket = _resSocket;
          });
          sendEstablished();
          return;
        }
        pluginMgr.postStats(req);
        reqEmitter = new EventEmitter();
        var headers = req.headers;
        var reqData = {
          ip: req.clientIp,
          method: util.toUpperCase(req.method) || 'CONNECT',
          httpVersion: req.httpVersion || '1.1',
          headers: headers
        };
        var resData = {headers: {}};
        data = reqEmitter.data = {
          url: options.host,
          startTime: Date.now(),
          rules: _rules,
          req: reqData,
          res: resData,
          isHttps: true
        };
        if (!filter.hide) {
          proxy.emit('request', reqEmitter);
        }
        if (disable.tunnel) {
          return reqSocket.destroy();
        }

        var statusCode = util.getMatcherValue(_rules.statusCode);
        if (statusCode) {
          return sendEstablished(statusCode);
        }

        pluginMgr.loadPlugin(plugin, function() {
          var tunnelPort, proxyUrl;
          if(plugin) {
            tunnelPort = plugin.ports && plugin.ports.tunnelPort;
            if (!tunnelPort) {
              return emitError(new Error('No plugin.tunnelServer'));
            }
            proxyUrl = 'proxy://127.0.0.1:' + tunnelPort;
          }

          var realUrl = _rules.rule && _rules.rule.url.replace('https:', 'tunnel:');
          if (/^tunnel:\/\//.test(realUrl) && realUrl != tunnelUrl) {
            tunnelUrl = realUrl;
            data.realUrl = realUrl.replace('tunnel://', '');
            parseUrl();
          }

          rules.getProxy(tunnelUrl, proxyUrl ? null : req, function(err, hostIp, hostPort) {
            if (!proxyUrl) {
              proxyUrl = _rules.proxy ? _rules.proxy.matcher : null;
            }

            if (proxyUrl) {
              var isSocks = /^socks:\/\//.test(proxyUrl);
              var _url = 'http:' + util.removeProtocol(proxyUrl);
              data.proxy = true;
              resolveHost(_url, function(ip) {
                options = parseUrl(_url, isSocks ? 1080 : 80);
                var isProxyPort = options.port == config.port;
                if (isProxyPort && util.isLocalAddress(ip)) {
                  return emitError(new Error('Unable to proxy to itself'));
                }
                var onConnect = function(_proxySocket) {
                  proxySocket = _proxySocket;
                  abortIfUnavailable(proxySocket);
                  reqSocket.pipe(proxySocket).pipe(reqSocket);
                  sendEstablished();
                };
                var dstOptions = url.parse(tunnelUrl);
                dstOptions.proxyHost = ip;
                dstOptions.proxyPort = parseInt(options.port, 10);
                dstOptions.port = dstOptions.port || 443;
                dstOptions.host = dstOptions.hostname;
                var _headers = extend({}, headers);
                if (req.clientIp) {
                  _headers[pluginMgr.CLIENT_IP_HEADER] = req.clientIp;
                }
                var ruleValue = tunnelPort && util.getMatcherValue(_rules.rule);
                if (ruleValue) {
                  _headers[pluginMgr.RULE_VALUE_HEADER] = encodeURIComponent(ruleValue);
                }
                _headers.host = dstOptions.hostname + ':' + (dstOptions.port || 443);
                dstOptions.headers = _headers;
                if (isSocks) {
                  dstOptions.proxyPort = options.port || 1080;
                  dstOptions.localDNS = false;
                  dstOptions.auths = config.getAuths(options);
                  socks.connect(dstOptions, onConnect).on('error', emitError);
                } else {
                  dstOptions.proxyPort = options.port || 80;
                  dstOptions.proxyAuth = options.auth;
                  if (isProxyPort) {
                    _headers[config.WEBUI_HEAD] = 1;
                  }
                  config.connect(dstOptions, onConnect).on('error', emitError);
                }
              });
            } else {
              tunnel(hostIp, hostPort);
            }
          });
        });

        function tunnel(hostIp, hostPort) {
          resolveHost(tunnelUrl, function(ip, port) {
            resData.ip = port ? ip + ':' + port : ip;
            resSocket = net.connect(port || options.port, ip, function() {
              resSocket.pipe(reqSocket).pipe(resSocket);
              sendEstablished();
            });
            abortIfUnavailable(resSocket);
          }, hostIp, hostPort);
        }

        function resolveHost(url, callback, hostIp, hostPort) {
          var hostHandler = function(err, ip, port, host) {
            if (host) {
              _rules.host = host;
            }
            data.requestTime = data.dnsTime = Date.now();
            resData.ip = ip || LOCALHOST;
            reqEmitter.emit('send', data);
            err ? emitError(err) : callback(ip, port);
          };
          if (hostIp) {
            hostHandler(null, hostIp, hostPort);
          } else {
            rules.resolveHost(url, hostHandler, rulesMgr, req.rulesFileMgr);
          }
        }

        function abortIfUnavailable(socket) {
          socket.on('error', emitError).on('close', emitError);
        }

        function sendEstablished(code) {
          if (code) {
            reqSocket.end('HTTP/1.1 ' + code + ' ' + (STATUS_CODES[code] || 'unknown') + '\r\nProxy-Agent: ' + config.name + '\r\n\r\n');
          } else {
            reqSocket.write('HTTP/1.1 200 Connection Established\r\nProxy-Agent: ' + config.name + '\r\n\r\n');
          }
          if (reqEmitter) {
            responsed = true;
            data.responseTime = data.endTime = Date.now();
            resData.statusCode = code || 200;
            reqEmitter.emit('response', data);
            reqEmitter.emit('end', data);
          }
          return reqSocket;
        }

        function emitError(err) {
          if (responsed) {
            return;
          }
          responsed = true;
          resSocket && resSocket.destroy();
          proxySocket && proxySocket.destroy();
          reqSocket.destroy();
          if (!reqEmitter) {
            return;
          }
          data.responseTime = data.endTime = Date.now();

          if (!resData.ip) {
            resData.ip = LOCALHOST;
          }

          if (!err) {
            err = new Error('Aborted');
            data.reqError = true;
            resData.statusCode ='aborted';
            reqData.body = util.getErrorStack(err);
            reqEmitter.emit('abort', data);
          } else {
            data.resError = true;
            resData.statusCode = resData.statusCode || 502;
            resData.body = util.getErrorStack(err);
            util.emitError(reqEmitter, data);
          }
        }
      });
    });
  });

  return server;
}

function proxy(callback) {
  var app = express();
  var server = app.listen(config.port, callback);
  var proxyEvents = new EventEmitter();
  var middlewares = ['./init', '../biz']
    .concat(require('./inspectors'))
    .concat(config.middlewares)
    .concat(require('./handlers'));

  proxyEvents.config = config;
  app.logger = logger;
  middlewares.forEach(function(mw) {
    mw && app.use((typeof mw == 'string' ? require(mw) : mw).bind(proxyEvents));
  });

  exportInterfaces(proxyEvents);
  tunnelProxy(server, proxyEvents);
  initDataServer(proxyEvents);
  initLogServer(proxyEvents);
  require('../biz/init')(proxyEvents);
  config.debug && rules.disableDnsCache();
  config.rules && rulesUtil.parseRules(config.rules);
  if (config.values) {
    Object.keys(config.values).forEach(function(name) {
      rulesUtil.values.add(name, config.values[name]);
    });
  }
  return proxyEvents;
}

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

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

module.exports = exportInterfaces(proxy);
<commit_msg>refactor: Add rules headers to tunnel proxy headers<commit_after>var express = require('express');
var url = require('url');
var net = require('net');
var path = require('path');
var tls = require('tls');
var fs = require('fs');
var socks = require('socksv5');
var EventEmitter = require('events').EventEmitter;
var extend = require('util')._extend;
var util = require('./util');
var logger = require('./util/logger');
var rules = require('./rules');
var dispatch = require('./https');
var httpsUtil = require('./https/util');
var rulesUtil = require('./rules/util');
var initDataServer = require('./util/data-server');
var initLogServer = require('./util/log-server');
var pluginMgr = require('./plugins');
var config = require('./config');
var loadService = require('./service');
var LOCALHOST = '127.0.0.1';
var TUNNEL_HOST_RE = /^[^:\/]+\.[^:\/]+:\d+$/;
var STATUS_CODES = require('http').STATUS_CODES || {};
var index = 0;

function tunnelProxy(server, proxy) {
//see: https://github.com/joyent/node/issues/9272
  if (typeof tls.checkServerIdentity == 'function') {
    var checkServerIdentity = tls.checkServerIdentity;
    tls.checkServerIdentity = function() {
      try {
        return checkServerIdentity.apply(this, arguments);
      } catch(err) {
        logger.error(err);
        return err;
      }
    };
  }

  process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
  server.on('connect', function(req, reqSocket, head) {//ws, wss, https proxy
    var tunnelUrl = req.fullUrl = util.setProtocol(TUNNEL_HOST_RE.test(req.url) ? req.url : req.headers.host, true);
    var options;
    var parseUrl = function (_url, port) {
      _url = _url || tunnelUrl;
      options = req.options = url.parse(_url);
      options.port = options.port || port || 443;
      return options;
    };
    parseUrl();
    tunnelUrl = req.fullUrl = 'tunnel://' + options.host;
    var resSocket, proxySocket, responsed, reqEmitter, data, rulesMgr;
    req.isTunnel = true;
    req.clientIp = util.getClientIp(req) || LOCALHOST;
    req.reqId = ++index;
    var hostname = options.hostname;
    var policy = req.headers[config.WHISTLE_POLICY_HEADER];
    var useTunnelPolicy = policy == 'tunnel';
    var isLocalUIUrl = !useTunnelPolicy && (config.isLocalUIUrl(hostname) || config.isPluginUrl(hostname));
    var _rules = req.rules = isLocalUIUrl ? {} : rules.resolveRules(tunnelUrl);
    rules.resolveFileRules(req, function() {
      var plugin = pluginMgr.resolveWhistlePlugins(req);
      pluginMgr.getTunnelRules(req, function(_rulesMgr) {
        if (_rulesMgr) {
          rulesMgr = _rulesMgr;
          util.mergeRules(req, rulesMgr.resolveRules(tunnelUrl));
          plugin = pluginMgr.getPluginByRuleUrl(util.rule.getUrl(_rules.rule));
        }
        var filter = req.filter;
        var disable = req.disable;
        abortIfUnavailable(reqSocket);
        if (req.headers[config.WEBUI_HEAD]) {
          return reqSocket.destroy();
        }
        if (!useTunnelPolicy && (policy === 'intercept' || isLocalUIUrl || ((filter.https || filter.intercept
            || rulesUtil.properties.get('interceptHttpsConnects')) && !disable.intercept && !disable.https))) {
          dispatch(reqSocket, hostname, proxy, function(_resSocket) {
            resSocket = _resSocket;
          });
          sendEstablished();
          return;
        }
        pluginMgr.postStats(req);
        reqEmitter = new EventEmitter();
        var headers = req.headers;
        var reqData = {
          ip: req.clientIp,
          method: util.toUpperCase(req.method) || 'CONNECT',
          httpVersion: req.httpVersion || '1.1',
          headers: headers
        };
        var resData = {headers: {}};
        data = reqEmitter.data = {
          url: options.host,
          startTime: Date.now(),
          rules: _rules,
          req: reqData,
          res: resData,
          isHttps: true
        };
        if (!filter.hide) {
          proxy.emit('request', reqEmitter);
        }
        if (disable.tunnel) {
          return reqSocket.destroy();
        }

        var statusCode = util.getMatcherValue(_rules.statusCode);
        if (statusCode) {
          return sendEstablished(statusCode);
        }

        pluginMgr.loadPlugin(plugin, function() {
          var tunnelPort, proxyUrl;
          if(plugin) {
            tunnelPort = plugin.ports && plugin.ports.tunnelPort;
            if (!tunnelPort) {
              return emitError(new Error('No plugin.tunnelServer'));
            }
            proxyUrl = 'proxy://127.0.0.1:' + tunnelPort;
            headers[pluginMgr.FULL_URL_HEADER] = encodeURIComponent(tunnelUrl);
            if (req.reqId) {
              headers[pluginMgr.REQ_ID_HEADER] = req.reqId;
            }
            var localHost = _rules.host;
            if (localHost) {
              headers[pluginMgr.LOCAL_HOST_HEADER] = headers[pluginMgr.CUR_HOST_HEADER]
              = encodeURIComponent(util.removeProtocol(localHost.matcher, true)
                + (localHost.port ? ':' + localHost.port : ''));
            }
          }

          var realUrl = _rules.rule && _rules.rule.url.replace('https:', 'tunnel:');
          if (/^tunnel:\/\//.test(realUrl) && realUrl != tunnelUrl) {
            tunnelUrl = realUrl;
            data.realUrl = realUrl.replace('tunnel://', '');
            parseUrl();
          }

          rules.getProxy(tunnelUrl, proxyUrl ? null : req, function(err, hostIp, hostPort) {
            if (!proxyUrl) {
              proxyUrl = _rules.proxy ? _rules.proxy.matcher : null;
            }

            if (proxyUrl) {
              var isSocks = /^socks:\/\//.test(proxyUrl);
              var _url = 'http:' + util.removeProtocol(proxyUrl);
              data.proxy = true;
              resolveHost(_url, function(ip) {
                options = parseUrl(_url, isSocks ? 1080 : 80);
                var isProxyPort = options.port == config.port;
                if (isProxyPort && util.isLocalAddress(ip)) {
                  return emitError(new Error('Unable to proxy to itself'));
                }
                var onConnect = function(_proxySocket) {
                  proxySocket = _proxySocket;
                  abortIfUnavailable(proxySocket);
                  reqSocket.pipe(proxySocket).pipe(reqSocket);
                  sendEstablished();
                };
                var dstOptions = url.parse(tunnelUrl);
                dstOptions.proxyHost = ip;
                dstOptions.proxyPort = parseInt(options.port, 10);
                dstOptions.port = dstOptions.port || 443;
                dstOptions.host = dstOptions.hostname;
                var _headers = extend({}, headers);
                if (req.clientIp) {
                  _headers[pluginMgr.CLIENT_IP_HEADER] = req.clientIp;
                }
                var ruleValue = tunnelPort && util.getMatcherValue(_rules.rule);
                if (ruleValue) {
                  _headers[pluginMgr.RULE_VALUE_HEADER] = encodeURIComponent(ruleValue);
                }
                _headers.host = dstOptions.hostname + ':' + (dstOptions.port || 443);
                dstOptions.headers = _headers;
                if (isSocks) {
                  dstOptions.proxyPort = options.port || 1080;
                  dstOptions.localDNS = false;
                  dstOptions.auths = config.getAuths(options);
                  socks.connect(dstOptions, onConnect).on('error', emitError);
                } else {
                  dstOptions.proxyPort = options.port || 80;
                  dstOptions.proxyAuth = options.auth;
                  if (isProxyPort) {
                    _headers[config.WEBUI_HEAD] = 1;
                  }
                  config.connect(dstOptions, onConnect).on('error', emitError);
                }
              });
            } else {
              tunnel(hostIp, hostPort);
            }
          });
        });

        function tunnel(hostIp, hostPort) {
          resolveHost(tunnelUrl, function(ip, port) {
            resData.ip = port ? ip + ':' + port : ip;
            resSocket = net.connect(port || options.port, ip, function() {
              resSocket.pipe(reqSocket).pipe(resSocket);
              sendEstablished();
            });
            abortIfUnavailable(resSocket);
          }, hostIp, hostPort);
        }

        function resolveHost(url, callback, hostIp, hostPort) {
          var hostHandler = function(err, ip, port, host) {
            if (host) {
              _rules.host = host;
            }
            data.requestTime = data.dnsTime = Date.now();
            resData.ip = ip || LOCALHOST;
            reqEmitter.emit('send', data);
            err ? emitError(err) : callback(ip, port);
          };
          if (hostIp) {
            hostHandler(null, hostIp, hostPort);
          } else {
            rules.resolveHost(url, hostHandler, rulesMgr, req.rulesFileMgr);
          }
        }

        function abortIfUnavailable(socket) {
          socket.on('error', emitError).on('close', emitError);
        }

        function sendEstablished(code) {
          if (code) {
            reqSocket.end('HTTP/1.1 ' + code + ' ' + (STATUS_CODES[code] || 'unknown') + '\r\nProxy-Agent: ' + config.name + '\r\n\r\n');
          } else {
            reqSocket.write('HTTP/1.1 200 Connection Established\r\nProxy-Agent: ' + config.name + '\r\n\r\n');
          }
          if (reqEmitter) {
            responsed = true;
            data.responseTime = data.endTime = Date.now();
            resData.statusCode = code || 200;
            reqEmitter.emit('response', data);
            reqEmitter.emit('end', data);
          }
          return reqSocket;
        }

        function emitError(err) {
          if (responsed) {
            return;
          }
          responsed = true;
          resSocket && resSocket.destroy();
          proxySocket && proxySocket.destroy();
          reqSocket.destroy();
          if (!reqEmitter) {
            return;
          }
          data.responseTime = data.endTime = Date.now();

          if (!resData.ip) {
            resData.ip = LOCALHOST;
          }

          if (!err) {
            err = new Error('Aborted');
            data.reqError = true;
            resData.statusCode ='aborted';
            reqData.body = util.getErrorStack(err);
            reqEmitter.emit('abort', data);
          } else {
            data.resError = true;
            resData.statusCode = resData.statusCode || 502;
            resData.body = util.getErrorStack(err);
            util.emitError(reqEmitter, data);
          }
        }
      });
    });
  });

  return server;
}

function proxy(callback) {
  var app = express();
  var server = app.listen(config.port, callback);
  var proxyEvents = new EventEmitter();
  var middlewares = ['./init', '../biz']
    .concat(require('./inspectors'))
    .concat(config.middlewares)
    .concat(require('./handlers'));

  proxyEvents.config = config;
  app.logger = logger;
  middlewares.forEach(function(mw) {
    mw && app.use((typeof mw == 'string' ? require(mw) : mw).bind(proxyEvents));
  });

  exportInterfaces(proxyEvents);
  tunnelProxy(server, proxyEvents);
  initDataServer(proxyEvents);
  initLogServer(proxyEvents);
  require('../biz/init')(proxyEvents);
  config.debug && rules.disableDnsCache();
  config.rules && rulesUtil.parseRules(config.rules);
  if (config.values) {
    Object.keys(config.values).forEach(function(name) {
      rulesUtil.values.add(name, config.values[name]);
    });
  }
  return proxyEvents;
}

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

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

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


<commit_before>var Q = require('q');
var util = require('util');
var url = require('url');
var mout = require('mout');
var path = require('path');
var request = require('request');
var Config = require('bower-config');
var download = require('bower').util.download;
var createError = require('bower').util.createError;
var extract = require('bower').util.extract;
var Resolver = require('bower').resolverBase;

//TODO: implicit url - bower install arti://...

function ArtifactoryResolver(decEndpoint, config, logger) {
    var pair;
    Resolver.call(this, decEndpoint, config, logger);

    this._artifactoryRegistryAddress = ArtifactoryResolver.extractArtifactoryRegistry(this._config);

    pair = ArtifactoryResolver.getOrgRepoPair(this._source);
    if(!pair){
        throw createError('Invalid Artifactory Registry', 'EINVEND', {
            details: this._source + ' does not seem to be a valid Artifactory registry response!'
        });
    }
    this._org = pair.org;
    this._repo = pair.repo;
}

util.inherits(ArtifactoryResolver, Resolver);
mout.object.mixIn(ArtifactoryResolver, Resolver);

/*ArtifactoryResolver.resolverMatch = function (source){
    if(/^art?:\/\//i.exec(source))
        return true;

    return false;
}*/

// Abstract functions that must be implemented by concrete resolvers
ArtifactoryResolver.prototype._resolve = function () {
    var msg;
    var that = this;
    var requestUrl = this._artifactoryRegistryAddress + '/binaries/' + this._org + '/' + this._repo +
        '.git/' + this._target;

    requestUrl = url.format(requestUrl);
    var artifactoryRemote = url.parse(requestUrl);
    var tempDir = this._tempDir;
    var reqHeaders = {};
    var bowerConfig = this._config;
    var target = this._target;

    var resolve = ArtifactoryResolver.doArtifactoryHeadRequest(requestUrl, bowerConfig)
        .then(function (headResponse){
            that._response = headResponse;
            var contentDisposition = collectHeaderFileName(headResponse);
            var file = path.join(tempDir, contentDisposition);
            return download(requestUrl, file, {
                proxy: artifactoryRemote.protocol === 'https:' ? that._config.httpsProxy : that._config.proxy,
                strictSSL: that._config.strictSsl,
                timeout: that._config.timeout,
                headers: reqHeaders
            })
                .progress(function (state) {
                    // Retry?
                    if (state.retry) {
                        msg = 'Download of ' + requestUrl + ' failed with ' + state.error.code + ', ';
                        msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
                        that._logger.debug('error', state.error.message, { error: state.error });
                        return that._logger.warn('retry', msg);
                    }

                    // Progress
                    msg = 'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
                    if (state.total) {
                        msg += ' of ' + (state.total / 1024 / 1024).toFixed(1) + 'MB downloaded, ';
                        msg += state.percent + '%';
                    }
                    that._logger.info('progress', msg);
                })
                .then(function () {
                    // Extract archive
                    that._logger.action('extract', path.basename(file), {
                        archive: file,
                        to: that._tempDir
                    });

                    return extract(file, that._tempDir)
                        // Fallback to standard git clone if extraction failed
                        .fail(function (err) {
                            msg =  'Decompression of ' + path.basename(file) + ' failed' + (err.code ? ' with ' + err.code : '') + ', ';
                            that._logger.debug('error', err.message, { error: err });
                            that._logger.warn('retry', msg);

                            return that._cleanTempDir();
                        });
                    // Fallback to standard git clone if download failed
                }, function (err) {
                    msg = 'Download of ' + requestUrl + ' failed' + (err.code ? ' with ' + err.code : '') + ', ';
                    that._logger.debug('error', err.message, { error: err });
                    that._logger.warn('retry', msg);

                    return that._cleanTempDir();
                });
        })
        .fail(function(response){
            var status = response.statusCode;

            // In case we got 404, lets get the full error JSON, and show it to the user
            if(status === 404){
                return ArtifactoryResolver.doArtifactoryRequest(requestUrl, bowerConfig)
                    .then(function (response){
                        console.log("response" + response);
                        return true;
                    })
                    .fail(function(jsonResponse){
                        var err = createError('Tag/branch ' + target + ' does not exist', 'ENORESTARGET');
                        err.details = jsonResponse.message;
                        throw err;
                    });
            }

            if (status < 200 || status >= 300) {
                return createError('Request to ' + requestUrl + ' failed with ' + response.statusCode,
                    'EINVRES');
            }
        });

    return resolve;
};

ArtifactoryResolver.prototype._hasNew = function (canonicalDir, pkgMeta) {
    var requestUrl = this._artifactoryRegistryAddress + '/binaries/' + this._org + '/' + this._repo +
        '.git/' + this._target;

    var oldCacheHeaders = pkgMeta._cacheHeaders || {};
    var reqHeaders = {};

    // If the previous cache headers contain an ETag,
    // send the "If-None-Match" header with it
    if (oldCacheHeaders.ETag) {
        reqHeaders['If-None-Match'] = oldCacheHeaders.ETag;
    }

    var res = ArtifactoryResolver.doArtifactoryHeadRequest(requestUrl, this._config, reqHeaders)
        .then(function (response){
            console.log("response" + response);
            return true;
        })
        .fail(function(response){
            if (response.statusCode === 304) {
                return false;
            }

            return false;
        });

    return res;
}

ArtifactoryResolver.prototype._savePkgMeta = function (meta) {
    // Store collected headers in the package meta
    meta._cacheHeaders = this._collectCacheHeaders(this._response);

    // Store ETAG under _release
    if (meta._cacheHeaders.ETag) {
        meta._release = 'e-tag:' + mout.string.trim(meta._cacheHeaders.ETag.substr(0, 10), '"');
    }

    return Resolver.prototype._savePkgMeta.call(this, meta);
};

ArtifactoryResolver.getOrgRepoPair = function (url) {
    var match;
    match = url.replace(ArtifactoryResolver.ARTIFACTORY_PREFIX, "").split("/")

    if (match.length < 2) {
        return null;
    }

    return {
        org: match[0],
        repo: match[1]
    };
};

ArtifactoryResolver.extractArtifactoryRegistry = function (config){
    var registryUrl;
    registryUrl = config.registry.register;
    if(!registryUrl || registryUrl===Config.DEFAULT_REGISTRY){
        config.registry.search.forEach(function (reg) {
            if(reg.indexOf("artifactory") > -1){
                registryUrl = reg;
            }
        });
    }

    return registryUrl;
}

ArtifactoryResolver.doArtifactoryHeadRequest = function (requestUrl, config, customHeaders){
    var headers = customHeaders || {};
    var remote = url.parse(requestUrl);
    var deferred = Q.defer();

    request.head(requestUrl, {
        proxy: remote.protocol === 'https:' ? config.httpsProxy : config.proxy,
        headers: headers,
        strictSSL: config.strictSsl,
        timeout: config.timeout
    })
        .on('error', function (error){
            throw createError('Request to ' + requestUrl + ' failed: ' + error.message, error.code);
            //deferred.reject();
        })
        .on('response', function(response) {
            var status = response.statusCode;

            if (status < 200 || status >= 300) {
                return deferred.reject(response);
            }

            deferred.resolve(response);
        });

    return deferred.promise;
}

ArtifactoryResolver.doArtifactoryRequest = function (requestUrl, config){
    var headers = {};
    var remote = url.parse(requestUrl);
    var deferred = Q.defer();

    request.get(requestUrl, {
        proxy: remote.protocol === 'https:' ? config.httpsProxy : config.proxy,
        headers: headers,
        strictSSL: config.strictSsl,
        timeout: config.timeout
    })
        .on('error', function (error){
            deferred.reject(createError('Request to ' + requestUrl + ' failed: ' + error.message, error.code));
        })
        .on('data', function (data) {
            var jsonObject = JSON.parse(data);
            deferred.reject(jsonObject.errors[0]);
        })
    /*.on('response', function(response) {
     var status = response.statusCode;

     if(status !== 404){
     deferred.resolve(response)
     }

     *//* if (status < 200 || status >= 300) {
     return deferred.reject(createError('Request to ' + requestUrl + ' failed with ' + response.statusCode,
     'EINVRES'));
     }

     deferred.resolve(response);*//*
     });*/

    return deferred.promise;
}

function collectHeaderFileName(headResponse){
    var contentDisposition = headResponse.headers['content-disposition'];
    contentDisposition = contentDisposition.replace("attachment; filename=", "");
    contentDisposition = contentDisposition.substring(1, contentDisposition.length  - 1);

    return contentDisposition;
}

ArtifactoryResolver.prototype._collectCacheHeaders = function (res) {
    var headers = {};

    // Collect cache headers
    this.constructor._cacheHeaders.forEach(function (name) {
        var value = res.headers[name.toLowerCase()];

        if (value != null) {
            headers[name] = value;
        }
    });

    return headers;
};

ArtifactoryResolver._cacheHeaders = [
    'Content-MD5',
    'ETag',
    'Last-Modified',
    'Content-Language',
    'Content-Length',
    'Content-Type',
    'Content-Disposition'
];

ArtifactoryResolver.ARTIFACTORY_PREFIX = "arti://";

module.exports = ArtifactoryResolver;
<commit_msg>Update index.js<commit_after>var Q = require('q');
var util = require('util');
var url = require('url');
var mout = require('mout');
var path = require('path');
var request = require('request');
var Config = require('bower-config');
var download = require('bower-poc').util.download;
var createError = require('bower-poc').util.createError;
var extract = require('bower-poc').util.extract;
var Resolver = require('bower-poc').resolverBase;

//TODO: implicit url - bower install arti://...

function ArtifactoryResolver(decEndpoint, config, logger) {
    var pair;
    Resolver.call(this, decEndpoint, config, logger);

    this._artifactoryRegistryAddress = ArtifactoryResolver.extractArtifactoryRegistry(this._config);

    pair = ArtifactoryResolver.getOrgRepoPair(this._source);
    if(!pair){
        throw createError('Invalid Artifactory Registry', 'EINVEND', {
            details: this._source + ' does not seem to be a valid Artifactory registry response!'
        });
    }
    this._org = pair.org;
    this._repo = pair.repo;
}

util.inherits(ArtifactoryResolver, Resolver);
mout.object.mixIn(ArtifactoryResolver, Resolver);

/*ArtifactoryResolver.resolverMatch = function (source){
    if(/^art?:\/\//i.exec(source))
        return true;

    return false;
}*/

// Abstract functions that must be implemented by concrete resolvers
ArtifactoryResolver.prototype._resolve = function () {
    var msg;
    var that = this;
    var requestUrl = this._artifactoryRegistryAddress + '/binaries/' + this._org + '/' + this._repo +
        '.git/' + this._target;

    requestUrl = url.format(requestUrl);
    var artifactoryRemote = url.parse(requestUrl);
    var tempDir = this._tempDir;
    var reqHeaders = {};
    var bowerConfig = this._config;
    var target = this._target;

    var resolve = ArtifactoryResolver.doArtifactoryHeadRequest(requestUrl, bowerConfig)
        .then(function (headResponse){
            that._response = headResponse;
            var contentDisposition = collectHeaderFileName(headResponse);
            var file = path.join(tempDir, contentDisposition);
            return download(requestUrl, file, {
                proxy: artifactoryRemote.protocol === 'https:' ? that._config.httpsProxy : that._config.proxy,
                strictSSL: that._config.strictSsl,
                timeout: that._config.timeout,
                headers: reqHeaders
            })
                .progress(function (state) {
                    // Retry?
                    if (state.retry) {
                        msg = 'Download of ' + requestUrl + ' failed with ' + state.error.code + ', ';
                        msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
                        that._logger.debug('error', state.error.message, { error: state.error });
                        return that._logger.warn('retry', msg);
                    }

                    // Progress
                    msg = 'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
                    if (state.total) {
                        msg += ' of ' + (state.total / 1024 / 1024).toFixed(1) + 'MB downloaded, ';
                        msg += state.percent + '%';
                    }
                    that._logger.info('progress', msg);
                })
                .then(function () {
                    // Extract archive
                    that._logger.action('extract', path.basename(file), {
                        archive: file,
                        to: that._tempDir
                    });

                    return extract(file, that._tempDir)
                        // Fallback to standard git clone if extraction failed
                        .fail(function (err) {
                            msg =  'Decompression of ' + path.basename(file) + ' failed' + (err.code ? ' with ' + err.code : '') + ', ';
                            that._logger.debug('error', err.message, { error: err });
                            that._logger.warn('retry', msg);

                            return that._cleanTempDir();
                        });
                    // Fallback to standard git clone if download failed
                }, function (err) {
                    msg = 'Download of ' + requestUrl + ' failed' + (err.code ? ' with ' + err.code : '') + ', ';
                    that._logger.debug('error', err.message, { error: err });
                    that._logger.warn('retry', msg);

                    return that._cleanTempDir();
                });
        })
        .fail(function(response){
            var status = response.statusCode;

            // In case we got 404, lets get the full error JSON, and show it to the user
            if(status === 404){
                return ArtifactoryResolver.doArtifactoryRequest(requestUrl, bowerConfig)
                    .then(function (response){
                        console.log("response" + response);
                        return true;
                    })
                    .fail(function(jsonResponse){
                        var err = createError('Tag/branch ' + target + ' does not exist', 'ENORESTARGET');
                        err.details = jsonResponse.message;
                        throw err;
                    });
            }

            if (status < 200 || status >= 300) {
                return createError('Request to ' + requestUrl + ' failed with ' + response.statusCode,
                    'EINVRES');
            }
        });

    return resolve;
};

ArtifactoryResolver.prototype._hasNew = function (canonicalDir, pkgMeta) {
    var requestUrl = this._artifactoryRegistryAddress + '/binaries/' + this._org + '/' + this._repo +
        '.git/' + this._target;

    var oldCacheHeaders = pkgMeta._cacheHeaders || {};
    var reqHeaders = {};

    // If the previous cache headers contain an ETag,
    // send the "If-None-Match" header with it
    if (oldCacheHeaders.ETag) {
        reqHeaders['If-None-Match'] = oldCacheHeaders.ETag;
    }

    var res = ArtifactoryResolver.doArtifactoryHeadRequest(requestUrl, this._config, reqHeaders)
        .then(function (response){
            console.log("response" + response);
            return true;
        })
        .fail(function(response){
            if (response.statusCode === 304) {
                return false;
            }

            return false;
        });

    return res;
}

ArtifactoryResolver.prototype._savePkgMeta = function (meta) {
    // Store collected headers in the package meta
    meta._cacheHeaders = this._collectCacheHeaders(this._response);

    // Store ETAG under _release
    if (meta._cacheHeaders.ETag) {
        meta._release = 'e-tag:' + mout.string.trim(meta._cacheHeaders.ETag.substr(0, 10), '"');
    }

    return Resolver.prototype._savePkgMeta.call(this, meta);
};

ArtifactoryResolver.getOrgRepoPair = function (url) {
    var match;
    match = url.replace(ArtifactoryResolver.ARTIFACTORY_PREFIX, "").split("/")

    if (match.length < 2) {
        return null;
    }

    return {
        org: match[0],
        repo: match[1]
    };
};

ArtifactoryResolver.extractArtifactoryRegistry = function (config){
    var registryUrl;
    registryUrl = config.registry.register;
    if(!registryUrl || registryUrl===Config.DEFAULT_REGISTRY){
        config.registry.search.forEach(function (reg) {
            if(reg.indexOf("artifactory") > -1){
                registryUrl = reg;
            }
        });
    }

    return registryUrl;
}

ArtifactoryResolver.doArtifactoryHeadRequest = function (requestUrl, config, customHeaders){
    var headers = customHeaders || {};
    var remote = url.parse(requestUrl);
    var deferred = Q.defer();

    request.head(requestUrl, {
        proxy: remote.protocol === 'https:' ? config.httpsProxy : config.proxy,
        headers: headers,
        strictSSL: config.strictSsl,
        timeout: config.timeout
    })
        .on('error', function (error){
            throw createError('Request to ' + requestUrl + ' failed: ' + error.message, error.code);
            //deferred.reject();
        })
        .on('response', function(response) {
            var status = response.statusCode;

            if (status < 200 || status >= 300) {
                return deferred.reject(response);
            }

            deferred.resolve(response);
        });

    return deferred.promise;
}

ArtifactoryResolver.doArtifactoryRequest = function (requestUrl, config){
    var headers = {};
    var remote = url.parse(requestUrl);
    var deferred = Q.defer();

    request.get(requestUrl, {
        proxy: remote.protocol === 'https:' ? config.httpsProxy : config.proxy,
        headers: headers,
        strictSSL: config.strictSsl,
        timeout: config.timeout
    })
        .on('error', function (error){
            deferred.reject(createError('Request to ' + requestUrl + ' failed: ' + error.message, error.code));
        })
        .on('data', function (data) {
            var jsonObject = JSON.parse(data);
            deferred.reject(jsonObject.errors[0]);
        })
    /*.on('response', function(response) {
     var status = response.statusCode;

     if(status !== 404){
     deferred.resolve(response)
     }

     *//* if (status < 200 || status >= 300) {
     return deferred.reject(createError('Request to ' + requestUrl + ' failed with ' + response.statusCode,
     'EINVRES'));
     }

     deferred.resolve(response);*//*
     });*/

    return deferred.promise;
}

function collectHeaderFileName(headResponse){
    var contentDisposition = headResponse.headers['content-disposition'];
    contentDisposition = contentDisposition.replace("attachment; filename=", "");
    contentDisposition = contentDisposition.substring(1, contentDisposition.length  - 1);

    return contentDisposition;
}

ArtifactoryResolver.prototype._collectCacheHeaders = function (res) {
    var headers = {};

    // Collect cache headers
    this.constructor._cacheHeaders.forEach(function (name) {
        var value = res.headers[name.toLowerCase()];

        if (value != null) {
            headers[name] = value;
        }
    });

    return headers;
};

ArtifactoryResolver._cacheHeaders = [
    'Content-MD5',
    'ETag',
    'Last-Modified',
    'Content-Language',
    'Content-Length',
    'Content-Type',
    'Content-Disposition'
];

ArtifactoryResolver.ARTIFACTORY_PREFIX = "arti://";

module.exports = ArtifactoryResolver;
<|endoftext|>


<commit_before>'use strict';

var integration = require('@astronomerio/analytics.js-integration');

var Bloomreach = module.exports = integration('Bloom Reach')
.option('acctId', '')
.mapping('customMappings');

Bloomreach.prototype.page = function (page) {
    var name = page.name();
    var acctId = this.options.acctId;
    var br_data = {};
    br_data.acct_id = acctId;

    if (name === "Home") {
        br_data.ptype = 'homepage';
        br_data.cat_id = '';
        br_data.cat = '';
        br_data.prod_id = '';
        br_data.prod_name = '';
        br_data.sku = '';
        br_data.search_term = '';
        br_data.is_conversion = '0';
        br_data.basket_value = '';
        br_data.order_id = '';
    } else {
        br_data.ptype = 'other';
        br_data.cat_id = '';
        br_data.cat = '';
        br_data.prod_id = '';
        br_data.prod_name = '';
        br_data.sku = '';
        br_data.search_term = '';
        br_data.is_conversion = '0';
        br_data.basket_value = '';
        br_data.order_id = '';
    }

    br_data = br_data;
    this.fireTrackingPixel();
};

Bloomreach.prototype.productViewed = function (track) {
    var acctId = this.options.acctId;
    var br_data = {};

    br_data.acct_id = acctId;
    br_data.ptype = 'product';
    br_data.cat_id = '';
    br_data.cat = '';
    br_data.prod_id = track.id() || '';
    br_data.prod_name = document.title || track.properties().item_barcode || '';
    br_data.sku = track.sku() || track.properties().item_barcode;
    br_data.search_term = '';
    br_data.is_conversion = '0';
    br_data.basket_value = '';
    br_data.order_id = '';

    br_data = br_data;
    this.fireTrackingPixel();
};

Bloomreach.prototype.productListViewed = function (track) {
    var acctId = this.options.acctId;
    var br_data = {};

    br_data.acct_id = acctId;
    br_data.ptype = 'category';
    br_data.cat_id = track.id() || '';
    br_data.cat = track.category() || '';
    br_data.prod_id = '';
    br_data.prod_name = track.properties().item_barcode || '';
    br_data.sku = '';
    br_data.search_term = '';
    br_data.is_conversion = '';
    br_data.basket_value = '';
    br_data.order_id = '';

    br_data = br_data;
    this.fireTrackingPixel();
};

Bloomreach.prototype.fireTrackingPixel = function () {
    var acctId = this.options.acctId;
    var scriptElement = document.createElement('script');
    scriptElement.setAttribute('type','text/javascript');
    scriptElement.setAttribute('async', true);

    scriptElement.src = 'https:' == document.location.protocol ? "https://cdns.brsrvr.com/v1/br-trk-" + acctId + ".js" : "http://cdn.brcdn.com/v1/br-trk-" + acctId + ".js";
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(scriptElement, s);
};
<commit_msg>added back window<commit_after>'use strict';

var integration = require('@astronomerio/analytics.js-integration');

var Bloomreach = module.exports = integration('Bloom Reach')
.option('acctId', '')
.mapping('customMappings');

Bloomreach.prototype.page = function (page) {
    var name = page.name();
    var acctId = this.options.acctId;
    var br_data = {};
    br_data.acct_id = acctId;

    if (name === "Home") {
        br_data.ptype = 'homepage';
        br_data.cat_id = '';
        br_data.cat = '';
        br_data.prod_id = '';
        br_data.prod_name = '';
        br_data.sku = '';
        br_data.search_term = '';
        br_data.is_conversion = '0';
        br_data.basket_value = '';
        br_data.order_id = '';
    } else {
        br_data.ptype = 'other';
        br_data.cat_id = '';
        br_data.cat = '';
        br_data.prod_id = '';
        br_data.prod_name = '';
        br_data.sku = '';
        br_data.search_term = '';
        br_data.is_conversion = '0';
        br_data.basket_value = '';
        br_data.order_id = '';
    }

    window.br_data = br_data;
    this.fireTrackingPixel();
};

Bloomreach.prototype.productViewed = function (track) {
    var acctId = this.options.acctId;
    var br_data = {};

    br_data.acct_id = acctId;
    br_data.ptype = 'product';
    br_data.cat_id = '';
    br_data.cat = '';
    br_data.prod_id = track.id() || '';
    br_data.prod_name = document.title || track.properties().item_barcode || '';
    br_data.sku = track.sku() || track.properties().item_barcode;
    br_data.search_term = '';
    br_data.is_conversion = '0';
    br_data.basket_value = '';
    br_data.order_id = '';

    window.br_data = br_data;
    this.fireTrackingPixel();
};

Bloomreach.prototype.productListViewed = function (track) {
    var acctId = this.options.acctId;
    var br_data = {};

    br_data.acct_id = acctId;
    br_data.ptype = 'category';
    br_data.cat_id = track.id() || '';
    br_data.cat = track.category() || '';
    br_data.prod_id = '';
    br_data.prod_name = track.properties().item_barcode || '';
    br_data.sku = '';
    br_data.search_term = '';
    br_data.is_conversion = '';
    br_data.basket_value = '';
    br_data.order_id = '';

    window.br_data = br_data;
    this.fireTrackingPixel();
};

Bloomreach.prototype.fireTrackingPixel = function () {
    var acctId = this.options.acctId;
    var scriptElement = document.createElement('script');
    scriptElement.setAttribute('type','text/javascript');
    scriptElement.setAttribute('async', true);

    scriptElement.src = 'https:' == document.location.protocol ? "https://cdns.brsrvr.com/v1/br-trk-" + acctId + ".js" : "http://cdn.brcdn.com/v1/br-trk-" + acctId + ".js";
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(scriptElement, s);
};
<|endoftext|>


<commit_before>'use babel'

import {Numbers} from './numbers'

const Package = module.exports = {
  activate() {
    require('atom-package-deps').install()
  },
  provideIntentionsShow() {
    return {
      grammarScopes: ['*'],
      getIntentions({textEditor}) {
        const matches = []
        const textEditorElement = atom.views.getView(textEditor)
        for (const element of Array.from(textEditorElement.querySelectorAll('::shadow .numeric'))) {
          const position = element.getBoundingClientRect()
          const bufferPosition = textEditor.bufferPositionForScreenPosition(textEditorElement.component.screenPositionForMouseEvent({
            clientX: position.left,
            clientY: position.top
          }))
          matches.push({
            range: [[bufferPosition.row, bufferPosition.column], [bufferPosition.row, bufferPosition.column + element.textContent.length]],
            created: Package.intentionCreated
          })
        }
        return matches
      }
    }
  },
  intentionCreated({element, marker, textEditor}) {
    element.addEventListener('click', function() {
      textEditor.setCursorBufferPosition(marker.getBufferRange().start)
    })
    const something = [48, 22, 22]
    element.addEventListener('mousedown', function(e) {
      e.stopImmediatePropagation()
      e.preventDefault()
      const numbers = new Numbers({element, marker, textEditor})
      numbers.activate(e)
    })
  }
}
<commit_msg>:fire: General cleanup<commit_after>'use babel'

import {Numbers} from './numbers'

const Package = module.exports = {
  activate() {
    require('atom-package-deps').install()
  },
  provideIntentionsShow() {
    return {
      grammarScopes: ['*'],
      getIntentions({textEditor}) {
        const matches = []
        const textEditorElement = atom.views.getView(textEditor)
        for (const element of Array.from(textEditorElement.querySelectorAll('::shadow .numeric'))) {
          const position = element.getBoundingClientRect()
          const bufferPosition = textEditor.bufferPositionForScreenPosition(textEditorElement.component.screenPositionForMouseEvent({
            clientX: position.left,
            clientY: position.top
          }))
          matches.push({
            range: [[bufferPosition.row, bufferPosition.column], [bufferPosition.row, bufferPosition.column + element.textContent.length]],
            created: Package.intentionCreated
          })
        }
        return matches
      }
    }
  },
  intentionCreated({element, marker, textEditor}) {
    element.addEventListener('click', function() {
      textEditor.setCursorBufferPosition(marker.getBufferRange().start)
    })
    element.addEventListener('mousedown', function(e) {
      e.stopImmediatePropagation()
      e.preventDefault()
      const numbers = new Numbers({element, marker, textEditor})
      numbers.activate(e)
    })
  }
}
<|endoftext|>


<commit_before>import AjaxError from './AjaxError';
import on from '../common/on';
import partial from '../common/partial';
import { sendException } from '../support/fshGa';

let paused = true;
let queue = [];
let globalHandler;

function setOpts(options) {
  if (typeof options === 'string') {
    return { url: options };
  }
  return options;
}

function clearXhr(xhr) {
  xhr.abort();
  queue = [];
}

function beforeSend(xhr) {
  on(window, 'beforeunload', partial(clearXhr, xhr));
}

const ignoreStatus = [0, 503, 504];
const ignoreTextStatus = ['abort'];
const ignoreResponse = [
  'We have encountered an issue with a server connection',
  'We\'re performing maintenance on the game',
  'the team have been notified and will get it fixed soon',
  'uUDRezBqFM4',
];

function ignore(ajaxErr) {
  return ignoreStatus.includes(ajaxErr.jqXhr.status)
    || ignoreTextStatus.includes(ajaxErr.jqTextStatus)
    || ignoreResponse.some(
      (substring) => ajaxErr.jqXhr.responseText.includes(substring),
    );
}

function handleFailure(reject, ajaxErr) {
  if (!ignore(ajaxErr)) {
    sendException(ajaxErr.toString(), false);
    reject(ajaxErr);
  }
}

function failFilter([fn, opt, retries, resolve, reject]) {
  return function ajaxFail(jqXhr, textStatus, errorThrown) { // Closure
    if (retries > 0 && jqXhr.status === 503) {
      setTimeout(fn, 100, opt, retries - 1, resolve, reject);
    } else {
      handleFailure(reject,
        new AjaxError([opt, jqXhr, textStatus, errorThrown]));
    }
  };
}

function doAjax(options, retries, resolve, reject) {
  const opt = setOpts(options);
  opt.beforeSend = beforeSend;
  return $.ajax(opt).then(resolve)
    .catch(failFilter([doAjax, opt, retries, resolve, reject]));
}

function attemptTask(runner) {
  if ($.active < 4) {
    const opts = queue.shift();
    doAjax(...opts);
    runner();
  }
}

function taskRunner() {
  if (queue.length === 0) {
    paused = true;
  } else {
    paused = false;
    attemptTask(taskRunner);
  }
}

function initGlobalHandler() {
  if (!globalHandler) {
    $(document).ajaxComplete(taskRunner);
    globalHandler = true;
  }
}

function add(options, retries, resolve, reject) {
  queue.push([options, retries, resolve, reject]);
  if (paused) { taskRunner(); }
}

export default function retryAjax(options) {
  initGlobalHandler();
  if (options) {
    return new Promise(((resolve, reject) => {
      add(options, 10, resolve, reject);
    }));
  }
}
<commit_msg>increase concurrent XHR<commit_after>import AjaxError from './AjaxError';
import on from '../common/on';
import partial from '../common/partial';
import { sendException } from '../support/fshGa';

let paused = true;
let queue = [];
let globalHandler;

function setOpts(options) {
  if (typeof options === 'string') {
    return { url: options };
  }
  return options;
}

function clearXhr(xhr) {
  xhr.abort();
  queue = [];
}

function beforeSend(xhr) {
  on(window, 'beforeunload', partial(clearXhr, xhr));
}

const ignoreStatus = [0, 503, 504];
const ignoreTextStatus = ['abort'];
const ignoreResponse = [
  'We have encountered an issue with a server connection',
  'We\'re performing maintenance on the game',
  'the team have been notified and will get it fixed soon',
  'uUDRezBqFM4',
];

function ignore(ajaxErr) {
  return ignoreStatus.includes(ajaxErr.jqXhr.status)
    || ignoreTextStatus.includes(ajaxErr.jqTextStatus)
    || ignoreResponse.some(
      (substring) => ajaxErr.jqXhr.responseText.includes(substring),
    );
}

function handleFailure(reject, ajaxErr) {
  if (!ignore(ajaxErr)) {
    sendException(ajaxErr.toString(), false);
    reject(ajaxErr);
  }
}

function failFilter([fn, opt, retries, resolve, reject]) {
  return function ajaxFail(jqXhr, textStatus, errorThrown) { // Closure
    if (retries > 0 && jqXhr.status === 503) {
      setTimeout(fn, 100, opt, retries - 1, resolve, reject);
    } else {
      handleFailure(reject,
        new AjaxError([opt, jqXhr, textStatus, errorThrown]));
    }
  };
}

function doAjax(options, retries, resolve, reject) {
  const opt = setOpts(options);
  opt.beforeSend = beforeSend;
  return $.ajax(opt).then(resolve)
    .catch(failFilter([doAjax, opt, retries, resolve, reject]));
}

function attemptTask(runner) {
  if ($.active < 5) {
    const opts = queue.shift();
    doAjax(...opts);
    runner();
  }
}

function taskRunner() {
  if (queue.length === 0) {
    paused = true;
  } else {
    paused = false;
    attemptTask(taskRunner);
  }
}

function initGlobalHandler() {
  if (!globalHandler) {
    $(document).ajaxComplete(taskRunner);
    globalHandler = true;
  }
}

function add(options, retries, resolve, reject) {
  queue.push([options, retries, resolve, reject]);
  if (paused) { taskRunner(); }
}

export default function retryAjax(options) {
  initGlobalHandler();
  if (options) {
    return new Promise(((resolve, reject) => {
      add(options, 10, resolve, reject);
    }));
  }
}
<|endoftext|>


<commit_before>'use strict';

var express = require('express'),
	swintHelper = require('swint-helper'),
	defaultize = swintHelper.defaultize,
	walk = swintHelper.walk,
	path = require('path');

module.exports = function(options) {
	defaultize({
		dir: path.join(path.dirname(require.main.filename), 'router')
	}, options);

	this.options = options;

	this.routeList = null;
	this.expRouter = null;
};

var _ = module.exports.prototype;

_.load = function(middleware) {
	this.middleware = middleware;
	this.initRouter();
	this.setRouter();
};

_.initRouter = function() {
	this.routeList = walk({
		dir: this.options.dir
	});
};

_.setRouter = function() {
	var that = this;

	this.expRouter = express.Router();

	this.routeList.forEach(function(filePath) {
		var target = defaultize({
				info: {
					url: '/swint',
					method: 'get'
				},
				preMiddleware: [],
				postMiddleware: [],
				main: function(req, res, next) {
					next();
				}
			}, require(filePath)),
			args = [];

		args.push(target.info.url);

		target.preMiddleware.forEach(function(m) {
			if (typeof m === 'object') {
				args.push(that.middleware[m.name](m.options));
			} else {
				if (that.middleware[m].length === 3) {
					args.push(that.middleware[m]);
				} else if (that.middleware[m].length <= 1) {
					args.push(that.middleware[m]({}));
				}
			}
		});

		const joiFileName = `_${path.basename(filePath).split('.')[0]}.joi.js`;
		let pathURL = filePath.split('/');
		pathURL.pop(); // remove origin.js
		pathURL = pathURL.join('/');
		const joiURL = `${pathURL}/${joiFileName}`; // add _origin.joi.js

		if(that.middleware.hasOwnProperty('inputValidator')){
			if(fs.existsSync(joiURL)){
				args.push(that.middleware['inputValidator'](require(joiURL).inputValidator.options));
			}
		}

		args.push(target.main);

		if(that.middleware.hasOwnProperty('outputValidator')){
			if(fs.existsSync(joiURL)){
				args.push(that.middleware['outputValidator'](require(joiURL).outputValidator.options));
			}
		}

		target.postMiddleware.forEach(function(m) {
			if (typeof m === 'object') {
				args.push(that.middleware[m.name](m.options));
			} else {
				if (that.middleware[m].length === 3) {
					args.push(that.middleware[m]);
				} else if (that.middleware[m].length <= 1) {
					args.push(that.middleware[m]({}));
				}
			}
		});

		args.push(function(req, res, next) {
			req.not404 = true;
			next();
		});

		that.expRouter[target.info.method].apply(that.expRouter, args);
	});
};
<commit_msg>Fix: lint 체크, fs 추가<commit_after>'use strict';

var express = require('express'),
	swintHelper = require('swint-helper'),
	defaultize = swintHelper.defaultize,
	walk = swintHelper.walk,
	path = require('path');
const fs = require('fs');

module.exports = function (options) {
	defaultize({
		dir: path.join(path.dirname(require.main.filename), 'router')
	}, options);

	this.options = options;

	this.routeList = null;
	this.expRouter = null;
};

var _ = module.exports.prototype;

_.load = function (middleware) {
	this.middleware = middleware;
	this.initRouter();
	this.setRouter();
};

_.initRouter = function () {
	this.routeList = walk({
		dir: this.options.dir
	});
};

_.setRouter = function () {
	var that = this;

	this.expRouter = express.Router();

	this.routeList.forEach(function (filePath) {
		var target = defaultize({
				info: {
					url: '/swint',
					method: 'get'
				},
				preMiddleware: [],
				postMiddleware: [],
				main: function (req, res, next) {
					next();
				}
			}, require(filePath)),
			args = [];

		args.push(target.info.url);

		target.preMiddleware.forEach(function (m) {
			if (typeof m === 'object') {
				args.push(that.middleware[m.name](m.options));
			} else {
				if (that.middleware[m].length === 3) {
					args.push(that.middleware[m]);
				} else if (that.middleware[m].length <= 1) {
					args.push(that.middleware[m]({}));
				}
			}
		});

		const joiFileName = `_${path.basename(filePath).split('.')[0]}.joi.js`;
		let pathURL = filePath.split('/');
		pathURL.pop(); // remove origin.js
		pathURL = pathURL.join('/');
		const joiURL = `${pathURL}/${joiFileName}`; // add _origin.joi.js

		if (that.middleware.hasOwnProperty('inputValidator')) {
			if (fs.existsSync(joiURL)) {
				args.push(that.middleware['inputValidator'](require(joiURL).inputValidator.options));
			}
		}

		args.push(target.main);

		if (that.middleware.hasOwnProperty('outputValidator')) {
			if (fs.existsSync(joiURL)) {
				args.push(that.middleware['outputValidator'](require(joiURL).outputValidator.options));
			}
		}

		target.postMiddleware.forEach(function (m) {
			if (typeof m === 'object') {
				args.push(that.middleware[m.name](m.options));
			} else {
				if (that.middleware[m].length === 3) {
					args.push(that.middleware[m]);
				} else if (that.middleware[m].length <= 1) {
					args.push(that.middleware[m]({}));
				}
			}
		});

		args.push(function (req, res, next) {
			req.not404 = true;
			next();
		});

		that.expRouter[target.info.method].apply(that.expRouter, args);
	});
};
<|endoftext|>