<commit_before>/**
 * Events:
 * dependenciesChange(differences, manifest, user, repo) - When one or more dependencies for a manifest change
 * retrieve(manifest, user, repo) - The first time a manifest is retrieved
 */

var events = require('events');
var moment = require('moment');
var GitHubApi = require('github');
var config = require('config');
var couchwatch = require('couchwatch');
var githubUrl = require('github-url');

var github = new GitHubApi({version: '3.0.0'});

if(config.github) {
	github.authenticate({
		type: 'basic',
		username: config.github.username,
		password: config.github.password
	});
}

var exports = new events.EventEmitter();

function Manifest(data) {
	this.data = data;
	this.expires = moment().add(Manifest.TTL);
}

Manifest.TTL = moment.duration({hours: 1});

var manifests = {};

function PackageDiff(name, version, previous) {
	this.name = name;
	this.version = version;
	this.previous = previous;
}

function getDependencyDiffs(deps1, deps2) {

	deps1 = deps1 || {};
	deps2 = deps2 || {};

	var diffs = [];

	// Check for deletions and changes
	Object.keys(deps1).forEach(function(key) {

		if(!deps2[key] && deps2[key] !== '') {

			// Dep has been deleted
			diffs.push(new PackageDiff(key, null, deps1[key]));

		} else if(deps1[key] != deps2[key]) {

			// Dep has been changed
			diffs.push(new PackageDiff(key, deps2[key], deps1[key]));
		}
	});

	// Check for additions
	Object.keys(deps2).forEach(function(key) {
		if(!deps1[key]) {
			diffs.push(new PackageDiff(key, deps2[key], null));
		}
	});

	return diffs;
}

/**
 * Prevent JSON.parse errors from going postal and killing us all.
 * Currently we smother SyntaxError and the like into a more manageable null.
 * We may do something more clever soon.
 *
 * @param body
 * @return {*}
 */
function parseManifest(body) {
	try {
		// JSON.parse will barf with a SyntaxError if the body is ill.
		return JSON.parse(body);
	} catch (error) {
		return null;
	}
}

exports.getManifest = function(user, repo, callback) {

	var manifest = manifests[user] ? manifests[user][repo] : null;

	if(manifest && manifest.expires > new Date()) {
		console.log('Using cached manifest', manifest.data.name, manifest.data.version);
		callback(null, JSON.parse(JSON.stringify(manifest.data)));
		return;
	}

	github.repos.getContent({user: user, repo: repo, path: 'package.json'}, function(err, resp) {

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

		var packageJson = new Buffer(resp.content, resp.encoding).toString();
		var data = parseManifest(packageJson);

		if(!data) {
			callback(new Error('Failed to parse package.json: ' + packageJson));
		} else {

			console.log('Got manifest', data.name, data.version);

			var oldManifest = manifest;
			var oldDependencies = oldManifest ? oldManifest.data.dependencies : {};

			manifest = new Manifest(data);

			manifests[user] = manifests[user] || {};
			manifests[user][repo] = manifest;

			callback(null, manifest.data);

			if(!oldManifest) {

				exports.emit('retrieve', JSON.parse(JSON.stringify(data)), user, repo);

			} else {

				var diffs = getDependencyDiffs(oldDependencies, data.dependencies);

				if(diffs.length) {
					exports.emit('dependenciesChange', diffs, JSON.parse(JSON.stringify(data)), user, repo);
				}
			}
		}
	});
};

/**
 * Set the TTL for cached manifests.
 *
 * @param {moment.duration} duration Time period the manifests will be cahced for, expressed as a moment.duration.
 */
exports.setCacheDuration = function(duration) {
	Manifest.TTL = duration;
};

// When a user publishes a project, they likely updated their project dependencies
couchwatch('http://isaacs.iriscouch.com/registry', -1).on('row', function (change) {
	var info = githubUrl(change.doc.repository);
	// Expire the cached manifest for this user/repo
	if (info && manifests[info.user] && manifests[info.user][info.project]) {
		console.log('Expiring cached manifest', info.user, info.project);
		manifests[info.user][info.project].expires = moment();
	}
})

module.exports = exports;<commit_msg>jshint Y U NO!<commit_after>/**
 * Events:
 * dependenciesChange(differences, manifest, user, repo) - When one or more dependencies for a manifest change
 * retrieve(manifest, user, repo) - The first time a manifest is retrieved
 */

var events = require('events');
var moment = require('moment');
var GitHubApi = require('github');
var config = require('config');
var couchwatch = require('couchwatch');
var githubUrl = require('github-url');

var github = new GitHubApi({version: '3.0.0'});

if(config.github) {
	github.authenticate({
		type: 'basic',
		username: config.github.username,
		password: <PASSWORD>
	});
}

var exports = new events.EventEmitter();

function Manifest(data) {
	this.data = data;
	this.expires = moment().add(Manifest.TTL);
}

Manifest.TTL = moment.duration({hours: 1});

var manifests = {};

function PackageDiff(name, version, previous) {
	this.name = name;
	this.version = version;
	this.previous = previous;
}

function getDependencyDiffs(deps1, deps2) {

	deps1 = deps1 || {};
	deps2 = deps2 || {};

	var diffs = [];

	// Check for deletions and changes
	Object.keys(deps1).forEach(function(key) {

		if(!deps2[key] && deps2[key] !== '') {

			// Dep has been deleted
			diffs.push(new PackageDiff(key, null, deps1[key]));

		} else if(deps1[key] != deps2[key]) {

			// Dep has been changed
			diffs.push(new PackageDiff(key, deps2[key], deps1[key]));
		}
	});

	// Check for additions
	Object.keys(deps2).forEach(function(key) {
		if(!deps1[key]) {
			diffs.push(new PackageDiff(key, deps2[key], null));
		}
	});

	return diffs;
}

/**
 * Prevent JSON.parse errors from going postal and killing us all.
 * Currently we smother SyntaxError and the like into a more manageable null.
 * We may do something more clever soon.
 *
 * @param body
 * @return {*}
 */
function parseManifest(body) {
	try {
		// JSON.parse will barf with a SyntaxError if the body is ill.
		return JSON.parse(body);
	} catch (error) {
		return null;
	}
}

exports.getManifest = function(user, repo, callback) {

	var manifest = manifests[user] ? manifests[user][repo] : null;

	if(manifest && manifest.expires > new Date()) {
		console.log('Using cached manifest', manifest.data.name, manifest.data.version);
		callback(null, JSON.parse(JSON.stringify(manifest.data)));
		return;
	}

	github.repos.getContent({user: user, repo: repo, path: 'package.json'}, function(err, resp) {

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

		var packageJson = new Buffer(resp.content, resp.encoding).toString();
		var data = parseManifest(packageJson);

		if(!data) {
			callback(new Error('Failed to parse package.json: ' + packageJson));
		} else {

			console.log('Got manifest', data.name, data.version);

			var oldManifest = manifest;
			var oldDependencies = oldManifest ? oldManifest.data.dependencies : {};

			manifest = new Manifest(data);

			manifests[user] = manifests[user] || {};
			manifests[user][repo] = manifest;

			callback(null, manifest.data);

			if(!oldManifest) {

				exports.emit('retrieve', JSON.parse(JSON.stringify(data)), user, repo);

			} else {

				var diffs = getDependencyDiffs(oldDependencies, data.dependencies);

				if(diffs.length) {
					exports.emit('dependenciesChange', diffs, JSON.parse(JSON.stringify(data)), user, repo);
				}
			}
		}
	});
};

/**
 * Set the TTL for cached manifests.
 *
 * @param {moment.duration} duration Time period the manifests will be cahced for, expressed as a moment.duration.
 */
exports.setCacheDuration = function(duration) {
	Manifest.TTL = duration;
};

// When a user publishes a project, they likely updated their project dependencies
couchwatch('http://isaacs.iriscouch.com/registry', -1).on('row', function (change) {
	var info = githubUrl(change.doc.repository);
	// Expire the cached manifest for this user/repo
	if (info && manifests[info.user] && manifests[info.user][info.project]) {
		console.log('Expiring cached manifest', info.user, info.project);
		manifests[info.user][info.project].expires = moment();
	}
});

module.exports = exports;
{'fraction_non_alphanumeric': 0.15050830647160923, 'fraction_numerical': 0.005454996280684354, 'mean_word_length': 4.191119691119691, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 6, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5468014', 'n_tokens_mistral': 2818, 'n_tokens_neox': 2562, 'n_words': 658}