<commit_before>"use strict";

var util = require('util');
var Bugme = module.exports = {};

// create cache name
function cacheName(name) {
	return '___bugme_' + name;
}

/**
 * Create wrapper method for tracking method calls.
 * Prints entry and leave messages and method run time.
 */
function newTrackMethod(obj, name, cached) {
	return function() {
		var args = Array.prototype.slice.call(arguments);
		var time = +new Date();
		console.log(util.format(" => [ENTER] %s.%s %s", obj.constructor.name, name, util.inspect(args)));
		var ret = this[cached].apply(this, arguments);
		var elapsed = +new Date() - time;
		console.log(util.format(" <= [LEAVE] %s.%s (%d ms) => %s", obj.constructor.name, name, elapsed, util.inspect(ret)));
		return ret;
	};
}

/**
 * Wrap all object functions with tracker methods.
 */
Bugme.track = function(obj) {
	if (typeof obj === 'function') {
		obj = obj.prototype;
	}

	for (var prop in obj) {
		var cache = cacheName(prop);

		if (typeof obj[prop] === 'function') {
			obj[cache] = obj[prop];
			obj[prop] = newTrackMethod(obj, prop, cache);
		}
	}
};

/**
 * Remove all tracker methods from object.
 * Restoring orginal methods.
 */
Bugme.untrack = function(obj) {
	if (typeof obj === 'function') {
		obj = obj.prototype;
	}

	for (var prop in obj) {
		var cache = cacheName(prop);

		if (obj[cache] && typeof obj[prop] === 'function') {
			obj[prop] = obj[cache];
			obj[cache] = undefined;
			delete obj[cache];
		}
	}
};

/**
 * Dump object to stdout
 */
Bugme.dump = function(obj, depth) {
	console.log(util.inspect(obj, { showHidden: true, colors: true, depth: depth}));
};
<commit_msg>Add include, exclude and recursive options<commit_after>"use strict";

var util = require('util');
var Bugme = module.exports = {};

// create cache name
function cacheName(name) {
	return '___bugme_' + name;
}

/**
 * Create wrapper method for tracking method calls.
 * Prints entry and leave messages and method run time.
 */
function newTrackMethod(obj, name, cached) {
	return function() {
		var args = Array.prototype.slice.call(arguments);
		var time = +new Date();
		console.log(util.format(" => [ENTER] %s.%s %s", obj.constructor.name, name, util.inspect(args)));
		var ret = this[cached].apply(this, arguments);
		var elapsed = +new Date() - time;
		console.log(util.format(" <= [LEAVE] %s.%s (%d ms) => %s", obj.constructor.name, name, elapsed, util.inspect(ret)));
		return ret;
	};
}

/**
 * Wrap all object functions with tracker methods.
 */
Bugme.track = function(obj, opt, parent) {
	function buildFilter(list, def) {
		if (!list) return function() { return def; };

		if (typeof list === 'string') {
					var re = new RegExp(list);
					return re.test.bind(re);
		}

		if (Array.isArray(list)) {
			var regs = list.map(function(i) {
				if (typeof i === 'string')
					var re = new RegExp(i);
					return re.test.bind(re);
				return i;
			});
			return function(v) {
				return regs.some(function(e) {
					return e(v);
				});
			}
		};
		return list;
	}

	opt = opt || {};

	var include = buildFilter(opt.include, true);
	var exclude = buildFilter(opt.exclude, false);
	var recursive = !!opt.recursive;

	for (var prop in obj) {
		var name = (parent ? parent + '.' : '') + prop;
		var cache = cacheName(prop);

		if (typeof obj[prop] === 'function') {
			if (include(prop) && !exclude(prop)) {
				obj[cache] = obj[prop];
				obj[prop] = newTrackMethod(obj, name, cache);

				// clone cache object properties to tracker
				for (var p in obj[cache]) {
					obj[prop][p] = obj[cache][p];
				}
			}
			if (recursive) Bugme.track(obj[prop], opt, name);
		}
		if (typeof obj[prop] === 'object') {
			if (recursive) Bugme.track(obj[prop], opt, name);
		}
	}
};

/**
 * Remove all tracker methods from object.
 * Restoring orginal methods.
 */
Bugme.untrack = function(obj) {
	for (var prop in obj) {
		var cache = cacheName(prop);

		// untrack recursively
		if (typeof obj[prop] === 'function' || typeof obj[prop] === 'object') {
			Bugme.untrack(obj[prop]);
		}

		if (obj[cache] && typeof obj[prop] === 'function') {
			obj[prop] = obj[cache];
			obj[cache] = undefined;
			delete obj[cache];
		}
	}
};

/**
 * Dump object to stdout
 */
Bugme.dump = function(obj, depth) {
	console.log(util.inspect(obj, { showHidden: true, colors: true, depth: depth}));
};
<|endoftext|>


<commit_before>var express = require('express');
var bodyParser = require('body-parser');
var sqlite = require('sqlite3');
var shortid = require('shortid');
var urlapi = require('url');

var app = express();
const PORT = 1714;
const ROUTE_LIFESPAN = 600;
const DB_NAME = 'routes.db';

app.set('view engine', 'pug');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

//initialize in-memory database
var db = new sqlite.Database(DB_NAME);
db.run("CREATE TABLE Routes (id TEXT, url TEXT, hits INTEGER, created_on INTEGER)");

app.get('/', function(req, res) {
	res.render('index', { title: 'URL Shortener' });
});

app.post('/submit', function(req, res) {
	var url = req.body.url;
	var newID = shortid.generate();
	var timestamp = new Date().getTime() / 1000;
	var hits = 0;
	db.serialize(function() {
		var stmt = db.prepare("INSERT INTO Routes VALUES (?, ?, ?, ?)");
		stmt.run(newID, url, hits, timestamp);
		stmt.finalize();
	});
	console.log('Record created: ' + newID + '/' + url + '/' + timestamp + '/' + hits);
	res.render("success", { url: urlapi.format({
			protocol: req.protocol,
			hostname: req.hostname,
			pathname: newID,
			port: PORT
		})
	});
});

app.get('/:key', function(req, res) {
	var key = req.params['key'];
	db.serialize(function() {
		var stmt = db.prepare("SELECT url FROM Routes WHERE id = ?");
		stmt.get(key, function(err, row) {
			if (typeof row != 'undefined') {
				res.writeHead(302, {
					'Location': row.url
				});
				res.end();
			} else {
				res.send('The requested route does not exist!');
			}
		});
		stmt.finalize();
	});
});

app.listen(PORT, function() {
	console.log('listening...');
});
<commit_msg>added comments to index<commit_after>var express = require('express');
var bodyParser = require('body-parser');
var sqlite = require('sqlite3');
var shortid = require('shortid');
var urlapi = require('url');

var app = express();
const PORT = 1714;
const ROUTE_LIFESPAN = 600;
const DB_NAME = 'routes.db';

//set up express server configuration
app.set('view engine', 'pug');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

//initialize sqlite database
var db = new sqlite.Database(DB_NAME);
db.run("CREATE TABLE Routes (id TEXT, url TEXT, hits INTEGER, created_on INTEGER)");

//root directory, show index page
app.get('/', function(req, res) {
	res.render('index', { title: 'URL Shortener' });
});

//handles POST requests to "/submit"
//used for creating new entries in routes database
app.post('/submit', function(req, res) {
	var url = req.body.url;
	var newID = shortid.generate();
	var timestamp = new Date().getTime() / 1000;
	var hits = 0;
	db.serialize(function() {
		var stmt = db.prepare("INSERT INTO Routes VALUES (?, ?, ?, ?)");
		stmt.run(newID, url, hits, timestamp);
		stmt.finalize();
	});
	console.log('Record created: ' + newID + '/' + url + '/' + timestamp + '/' + hits);
	res.render("success", { url: urlapi.format({
			protocol: req.protocol,
			hostname: req.hostname,
			pathname: newID,
			port: PORT
		})
	});
});

//handles requests for individual ID keys (shortid)
app.get('/:key', function(req, res) {
	var key = req.params['key'];
	db.serialize(function() {
		var stmt = db.prepare("SELECT url FROM Routes WHERE id = ?");
		stmt.get(key, function(err, row) {
			if (typeof row != 'undefined') {
				res.writeHead(302, {
					'Location': row.url
				});
				res.end();
			} else {
				res.send('The requested route does not exist!');
			}
		});
		stmt.finalize();
	});
});

app.listen(PORT, function() {
	console.log('listening...');
});
<|endoftext|>


<commit_before>/* eslint-env node */
'use strict';

const MarkdownResolver = require('broccoli-markdown-resolver');
const MergeTrees = require('broccoli-merge-trees');
const fs = require('fs');
const path = require('path');

module.exports = {
  name: 'ember-cli-markdown-resolver',

  included(app) {
    this.addonConfig = this.getAddonConfig(app);
    this.testAppPath = this.getTestAppPath();
    return this._super.included.apply(this, arguments);
  },

  treeForAddon(tree) {

    let folders = Object.keys(this.addonConfig.folders || {}).reduce((folders, key) => {
      return [
        ...folders,
        this.addonConfig.folders[key]
      ];
    }, []);

    let mdPaths = folders.reduce((paths, folder) => {
      let folderPathSegments = folder.split('/'),
          folderPath = path.join(this.app.project.root, this.testAppPath, ...folderPathSegments),
          folderExists = fs.existsSync(folderPath);

      return folderExists ?
        [...paths, folderPath] :
        [...paths];
    }, []);

    if (mdPaths.length > 0) {

      let mdFiles = new MarkdownResolver(mdPaths, {
        basePath: path.join(this.app.project.root, this.testAppPath),
        outputFile: 'files.js',
        trimExtensions: true
      });

      tree = new MergeTrees([tree, mdFiles]);
    }

    return this._super.treeForAddon.call(this, tree);
  },

  getAddonConfig(app) {
    return this.app.project.config(app.env)[this.name] || {};
  },

  getTestAppPath() {
    return typeof this.app.options.configPath === String ? './tests/dummy' : '';
  }
};
<commit_msg>fix(index.js): proper string type check<commit_after>/* eslint-env node */
'use strict';

const MarkdownResolver = require('broccoli-markdown-resolver');
const MergeTrees = require('broccoli-merge-trees');
const fs = require('fs');
const path = require('path');

module.exports = {
  name: 'ember-cli-markdown-resolver',

  included(app) {
    this.addonConfig = this.getAddonConfig(app);
    this.testAppPath = this.getTestAppPath();
    return this._super.included.apply(this, arguments);
  },

  treeForAddon(tree) {

    let folders = Object.keys(this.addonConfig.folders || {}).reduce((folders, key) => {
      return [
        ...folders,
        this.addonConfig.folders[key]
      ];
    }, []);

    let mdPaths = folders.reduce((paths, folder) => {
      let folderPathSegments = folder.split('/'),
          folderPath = path.join(this.app.project.root, this.testAppPath, ...folderPathSegments),
          folderExists = fs.existsSync(folderPath);

      return folderExists ?
        [...paths, folderPath] :
        [...paths];
    }, []);

    if (mdPaths.length > 0) {

      let mdFiles = new MarkdownResolver(mdPaths, {
        basePath: path.join(this.app.project.root, this.testAppPath),
        outputFile: 'files.js',
        trimExtensions: true
      });

      tree = new MergeTrees([tree, mdFiles]);
    }

    return this._super.treeForAddon.call(this, tree);
  },

  getAddonConfig(app) {
    return this.app.project.config(app.env)[this.name] || {};
  },

  getTestAppPath() {
    let configPath = this.app.options.configPath;
    return (typeof configPath === 'string' || configPath instanceof String) ? './tests/dummy' : '';
  }
};
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');

var _customErrors = [];

exports.registerError = (name, errorCode) => {
  if (!name) {
    throw new Error('Custom error name is required');
  }
  if (_.some(_customErrors, { name })) {
    throw new Error(`Custom error ${name} is already registered`);
  }

  name = _.upperFirst(name);

  var CustomError = function(msg, info) {
    this.message = msg;
    this.info = info;
    this.errorCode = errorCode;
    this.name = name;
  };
  CustomError.prototype = new Error();

  exports[`is${name}Error`] = (err) => {
    return err instanceof CustomError;
  };
  exports[`get${name}Error`] = (msg, info) => {
    return new CustomError(msg, info);
  };
  exports[`rejectWith${name}Error`] = (msg, info) => {
    var err = exports[`get${name}Error`](msg, info);
    return Promise.reject(err);
  };

  _customErrors.push({
    name,
    type: CustomError
  });
};

exports.isKnownError = (err) => {
  return _.some(_customErrors, custErr => err instanceof custErr.type);
};
<commit_msg>add message template support<commit_after>'use strict';

var _ = require('lodash');

var _customErrors = [];

exports.registerError = (name, statusCode, messageTemplate) => {
  _validate(name);
  name = _.upperFirst(name);
  var CustomError = _createCustomError(name, statusCode);
  _createFunctions(name, CustomError, messageTemplate);

  _customErrors.push({
    name,
    type: CustomError
  });
};

exports.isKnownError = (err) => {
  return _.some(_customErrors, custErr => err instanceof custErr.type);
};

function _validate(name) {
  if (!name) {
    throw new Error('Custom error name is required');
  }
  if (_.some(_customErrors, { name })) {
    throw new Error(`Custom error ${name} is already registered`);
  }
}

function _createCustomError(name, statusCode) {
  var CustomError = function(msg) {
    this.name = name + 'Error';
    this.message = msg;
    this.statusCode = statusCode;
  };
  CustomError.prototype = new Error();

  return CustomError;
}

function _createFunctions(name, CustomError, messageTemplate) {
  exports[`is${name}Error`] = function(err) {
    return err instanceof CustomError;
  };

  exports[`get${name}Error`] = function(msg) {
    if (messageTemplate && !_.isString(msg) && _.isObject(msg)) {
      var msgParams = msg;
      msg = _.reduce(msgParams, (result, paramVal, paramKey) => {
        result = _.replace(result, '${' + paramKey + '}', paramVal);
        return result;
      }, messageTemplate);
    }
    return new CustomError(msg);
  };

  exports[`rejectWith${name}Error`] = function(msg) {
    var err = exports[`get${name}Error`](msg);
    return Promise.reject(err);
  };
}
<|endoftext|>


<commit_before>// HackerNews API - https://github.com/HackerNews/API

// https://github.com/stefanpenner/es6-promise#auto-polyfill
require('es6-promise').polyfill();
// https://github.com/matthew-andrews/isomorphic-fetch#usage,
require('isomorphic-fetch');
const bestStories = [];
const rating = 600;
let i;

function jsonFetch(url) {
  const opts = {
    method: 'get',
    // mode: 'cors',
    // credentials: 'include',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
  };

  return fetch(url, opts)
    .then((response) => {
      const { status } = response;
      if (status >= 400) {
        throw new Error(`Bad response from server with status ${status}`);
      }
      return response.json();
    });
}

const urlHN = 'https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty';
jsonFetch(urlHN)
  .then((stories) => {
    // const storyId = stories[0];
    for (i = 0; i <= stories.length; i++) {
      // console.log(`Now i is ${i} ; and stories.length is - ${stories.length}`);
      const storyId = stories[i];
      const storyUrl = `https://hacker-news.firebaseio.com/v0/item/${storyId}.json?print=pretty`;
      jsonFetch(storyUrl)
      // .then(storyObj => console.log(storyObj.score, storyObj.title));
      .then((storyObj) => {
        if (storyObj.score > rating) {
          bestStories.push(storyObj);
        }
      });
    }
    // console.log(bestStories);
  })
  .catch((e) => console.error(e));
<commit_msg>playing with yc api still much to do<commit_after>// HackerNews API - https://github.com/HackerNews/API

// https://github.com/stefanpenner/es6-promise#auto-polyfill
require('es6-promise').polyfill();
// https://github.com/matthew-andrews/isomorphic-fetch#usage,
require('isomorphic-fetch');
const bestStories = [];
const rating = 800;
let i;

setInterval(function testing() {
  console.log(bestStories);
}, 1000);

function jsonFetch(url) {
  const opts = {
    method: 'get',
    // mode: 'cors',
    // credentials: 'include',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
  };

  return fetch(url, opts)
    .then((response) => {
      const { status } = response;
      if (status >= 400) {
        throw new Error(`Bad response from server with status ${status}`);
      }
      return response.json();
    });
}

const urlHN = 'https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty';
jsonFetch(urlHN)
  .then((stories) => {
    for (i = 0; i < stories.length; i++) {
      // console.log(`Now i is ${i} ; and stories.length is - ${stories.length}`);
      const storyId = stories[i];
      const storyUrl = `https://hacker-news.firebaseio.com/v0/item/${storyId}.json?print=pretty`;
      jsonFetch(storyUrl)
        .then((storyObj) => {
          if (storyObj.score > rating) {
            bestStories.push(storyObj);
          }
        });
    }
    // console.log(bestStories);
  })
  .catch((e) => console.error(e));
// TODO: - save news on a json file
// TODO: save info from that json to mongo
// TODO:  play with mongo
<|endoftext|>


<commit_before>'use strict';

var constant          = require('es5-ext/function/constant')
  , setPrototypeOf    = require('es5-ext/object/set-prototype-of')
  , ensureString      = require('es5-ext/object/validate-stringifiable-value')
  , ensureObject      = require('es5-ext/object/valid-object')
  , d                 = require('d')
  , deferred          = require('deferred')
  , serialize         = require('dbjs/_setup/serialize/value')
  , MongoClient       = require('mongodb').MongoClient
  , MongoCursor       = require('mongodb/lib/cursor')
  , PersistenceDriver = require('dbjs-persistence/abstract')

  , create = Object.create, promisify = deferred.promisify
  , getUndefined = constant(undefined)
  , getNull = constant(null)
  , connect = promisify(MongoClient.connect)
  , updateOpts = { upsert: true };

Object.defineProperties(MongoCursor.prototype, {
	nextPromised: d(promisify(MongoCursor.prototype.next)),
	toArrayPromised: d(promisify(MongoCursor.prototype.toArray)),
	closePromised: d(promisify(MongoCursor.prototype.close))
});

var buildUrl = function (conf) {
	var url = 'mongodb://';
	if (conf.user && conf.password) url += conf.user + ':' + conf.password + '@';
	url += (conf.host != null) ? conf.host : 'localhost';
	url += ':';
	url += (conf.port != null) ? conf.port : '27017';
	return url + '/' + conf.database;
};
var MongoDriver = module.exports = function (dbjs, data) {
	if (!(this instanceof MongoDriver)) return new MongoDriver(dbjs, data);
	ensureObject(data);
	ensureString(data.database);
	ensureString(data.collection);
	PersistenceDriver.call(this, dbjs, data);
	this.mongoDb = connect(buildUrl(data)).aside(null, this.emitError);
	this.collection = this.mongoDb.invokeAsync('collection', data.collection);
};
setPrototypeOf(MongoDriver, PersistenceDriver);

MongoDriver.prototype = Object.create(PersistenceDriver.prototype, {
	constructor: d(MongoDriver),
	_getCustom: d(function (key) {
		return this.collection.invokeAsync('find', { _id: '_' + key })(function (cursor) {
			return cursor.nextPromised()(function (record) {
				return cursor.closePromised()(record ? record.value : getUndefined);
			});
		});
	}),
	_loadValue: d(function (id) {
		return this.collection.invokeAsync('find', { _id: id })(function (cursor) {
			return cursor.nextPromised()(function (record) {
				return cursor.closePromised()(record
					? this._importValue(id, record.value, record.stamp) : null);
			}.bind(this));
		}.bind(this));
	}),
	_load: d(function (query) {
		var count = 0;
		var promise = this.collection.invokeAsync('find', query)(function (cursor) {
			return cursor.toArrayPromised()(function (records) {
				return cursor.closePromised()(records.map(function (record) {
					var event;
					if (record._id[0] === '=') return; // computed record
					if (record._id[0] === '_') return; // custom record
					event = this._importValue(record._id, record.value, record.stamp);
					if (event && !(++count % 1000)) promise.emit('progress');
					return event;
				}, this).filter(Boolean));
			}.bind(this));
		}.bind(this));
		return promise;
	}),
	_loadObject: d(function (id) { return this._load({ _id: { $gte: id, $lt: id + '/\uffff' } }); }),
	_loadAll: d(function () { return this._load(); }),
	_storeCustom: d(function (key, value) {
		if (value === undefined) return this.collection.invokeAsync('remove', { _id: '_' + key });
		return this.collection.invokeAsync('update', { _id: '_' + key }, { value: value }, updateOpts);
	}),
	_storeEvent: d(function (event) {
		return this.collection.invokeAsync('update', { _id: event.object.__valueId__ }, {
			stamp: event.stamp,
			value: serialize(event.value)
		}, updateOpts);
	}),
	_storeEvents: d(function (events) { return deferred.map(events, this._storeEvent, this); }),
	_getComputed: d(function (id) {
		return this.collection.invokeAsync('find', { _id: '=' + id })(function (cursor) {
			return cursor.nextPromised()(function (record) {
				return cursor.closePromised()(record || getNull);
			});
		});
	}),
	_getAllComputed: d(function (keyPath) {
		var query = { _id: { $gte: '=', $lt: '=\uffff' } }, map = create(null);
		return this.collection.invokeAsync('find', query)(function (cursor) {
			return cursor.toArrayPromised()(function (records) {
				records.forEach(function (record) {
					var id = record._id.slice(1)
					  , objId = id.split('/', 1)[0], localKeyPath = id.slice(objId.length + 1);
					if (localKeyPath !== keyPath) return;
					map[id] = record;
				}, this);
				return cursor.closePromised();
			}.bind(this));
		}.bind(this))(map);
	}),
	_storeComputed: d(function (id, value, stamp) {
		return this.collection.invokeAsync('update', { _id: '=' + id }, {
			stamp: stamp,
			value: value
		}, updateOpts);
	}),
	_storeRaw: d(function (id, value) {
		if (id[0] === '_') return this._storeCustom(id.slice(1), value);
		if (id[0] === '=') return this._storeComputed(id.slice(1), value.value, value.stamp);
		return this.collection.invokeAsync('update', { _id: id },
			{ value: value.value, stamp: value.stamp }, updateOpts);
	}),
	_exportAll: d(function (destDriver) {
		var count = 0;
		var promise = this.collection.invokeAsync('find')(function (cursor) {
			return cursor.toArrayPromised()(function (records) {
				return cursor.closePromised()(records.map(function (record) {
					if (!(++count % 1000)) promise.emit('progress');
					return destDriver._storeRaw(record._id, record);
				}, this));
			}.bind(this));
		}.bind(this));
		return promise;
	}),
	_close: d(function () { return this.mongoDb.invokeAsync('close'); })
});
<commit_msg>Ensure proper promises resolution<commit_after>'use strict';

var constant          = require('es5-ext/function/constant')
  , setPrototypeOf    = require('es5-ext/object/set-prototype-of')
  , ensureString      = require('es5-ext/object/validate-stringifiable-value')
  , ensureObject      = require('es5-ext/object/valid-object')
  , d                 = require('d')
  , deferred          = require('deferred')
  , serialize         = require('dbjs/_setup/serialize/value')
  , MongoClient       = require('mongodb').MongoClient
  , MongoCursor       = require('mongodb/lib/cursor')
  , PersistenceDriver = require('dbjs-persistence/abstract')

  , create = Object.create, promisify = deferred.promisify
  , getUndefined = constant(undefined)
  , getNull = constant(null)
  , connect = promisify(MongoClient.connect)
  , updateOpts = { upsert: true };

Object.defineProperties(MongoCursor.prototype, {
	nextPromised: d(promisify(MongoCursor.prototype.next)),
	toArrayPromised: d(promisify(MongoCursor.prototype.toArray)),
	closePromised: d(promisify(MongoCursor.prototype.close))
});

var buildUrl = function (conf) {
	var url = 'mongodb://';
	if (conf.user && conf.password) url += conf.user + ':' + conf.password + '@';
	url += (conf.host != null) ? conf.host : 'localhost';
	url += ':';
	url += (conf.port != null) ? conf.port : '27017';
	return url + '/' + conf.database;
};
var MongoDriver = module.exports = function (dbjs, data) {
	if (!(this instanceof MongoDriver)) return new MongoDriver(dbjs, data);
	ensureObject(data);
	ensureString(data.database);
	ensureString(data.collection);
	PersistenceDriver.call(this, dbjs, data);
	this.mongoDb = connect(buildUrl(data)).aside(null, this.emitError);
	this.collection = this.mongoDb.invokeAsync('collection', data.collection);
};
setPrototypeOf(MongoDriver, PersistenceDriver);

MongoDriver.prototype = Object.create(PersistenceDriver.prototype, {
	constructor: d(MongoDriver),
	_getCustom: d(function (key) {
		return this.collection.invokeAsync('find', { _id: '_' + key })(function (cursor) {
			return cursor.nextPromised()(function (record) {
				return cursor.closePromised()(record ? record.value : getUndefined);
			});
		});
	}),
	_loadValue: d(function (id) {
		return this.collection.invokeAsync('find', { _id: id })(function (cursor) {
			return cursor.nextPromised()(function (record) {
				return cursor.closePromised()(record
					? this._importValue(id, record.value, record.stamp) : null);
			}.bind(this));
		}.bind(this));
	}),
	_load: d(function (query) {
		var count = 0;
		var promise = this.collection.invokeAsync('find', query)(function (cursor) {
			return cursor.toArrayPromised()(function (records) {
				return cursor.closePromised()(records.map(function (record) {
					var event;
					if (record._id[0] === '=') return; // computed record
					if (record._id[0] === '_') return; // custom record
					event = this._importValue(record._id, record.value, record.stamp);
					if (event && !(++count % 1000)) promise.emit('progress');
					return event;
				}, this).filter(Boolean));
			}.bind(this));
		}.bind(this));
		return promise;
	}),
	_loadObject: d(function (id) { return this._load({ _id: { $gte: id, $lt: id + '/\uffff' } }); }),
	_loadAll: d(function () { return this._load(); }),
	_storeCustom: d(function (key, value) {
		if (value === undefined) return this.collection.invokeAsync('remove', { _id: '_' + key });
		return this.collection.invokeAsync('update', { _id: '_' + key }, { value: value }, updateOpts);
	}),
	_storeEvent: d(function (event) {
		return this.collection.invokeAsync('update', { _id: event.object.__valueId__ }, {
			stamp: event.stamp,
			value: serialize(event.value)
		}, updateOpts);
	}),
	_storeEvents: d(function (events) { return deferred.map(events, this._storeEvent, this); }),
	_getComputed: d(function (id) {
		return this.collection.invokeAsync('find', { _id: '=' + id })(function (cursor) {
			return cursor.nextPromised()(function (record) {
				return cursor.closePromised()(record || getNull);
			});
		});
	}),
	_getAllComputed: d(function (keyPath) {
		var query = { _id: { $gte: '=', $lt: '=\uffff' } }, map = create(null);
		return this.collection.invokeAsync('find', query)(function (cursor) {
			return cursor.toArrayPromised()(function (records) {
				records.forEach(function (record) {
					var id = record._id.slice(1)
					  , objId = id.split('/', 1)[0], localKeyPath = id.slice(objId.length + 1);
					if (localKeyPath !== keyPath) return;
					map[id] = record;
				}, this);
				return cursor.closePromised();
			}.bind(this));
		}.bind(this))(map);
	}),
	_storeComputed: d(function (id, value, stamp) {
		return this.collection.invokeAsync('update', { _id: '=' + id }, {
			stamp: stamp,
			value: value
		}, updateOpts);
	}),
	_storeRaw: d(function (id, value) {
		if (id[0] === '_') return this._storeCustom(id.slice(1), value);
		if (id[0] === '=') return this._storeComputed(id.slice(1), value.value, value.stamp);
		return this.collection.invokeAsync('update', { _id: id },
			{ value: value.value, stamp: value.stamp }, updateOpts);
	}),
	_exportAll: d(function (destDriver) {
		var count = 0;
		var promise = this.collection.invokeAsync('find')(function (cursor) {
			return cursor.toArrayPromised()(function (records) {
				return cursor.closePromised()(deferred.map(records, function (record) {
					if (!(++count % 1000)) promise.emit('progress');
					return destDriver._storeRaw(record._id, record);
				}, this));
			}.bind(this));
		}.bind(this));
		return promise;
	}),
	_close: d(function () { return this.mongoDb.invokeAsync('close'); })
});
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const {Readable} = require('stream');
const sysPath = require('path');
const {promisify} = require('util');
const picomatch = require('picomatch');
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);
const lstat = promisify(fs.lstat);

const BANG = '!';
const normalizeFilter = (filter) => {
  if (filter === undefined) return;
  if (typeof filter === "function") return filter;

  if (typeof filter === "string") {
    const glob = picomatch(filter.trim());
    return (entry) => glob(entry.basename);
  }

  if (Array.isArray(filter)) {
    const positive = [];
    const negative = [];
    for (const item of filter) {
      const trimmed = item.trim();
      if (trimmed.charAt(0) === BANG) {
        negative.push(picomatch(trimmed.slice(1)));
      } else {
        positive.push(picomatch(trimmed));
      }
    }

    if (negative.length) {
      if (positive.length) {
        return (entry) => positive.some(f => f(entry.basename)) &&
          !negative.every(f => f(entry.basename));
      } else {
        return (entry) => !negative.every(f => f(entry.basename));
      }
    } else {
      return (entry) => positive.some(f => f(entry.basename));
    }
  }
};

const NO_ENTRY_ERROR = 'ENOENT';
const FILE_TYPE = 'files';
const DIR_TYPE = 'directories';
const FILE_DIR_TYPE = 'both';
const ALL_TYPE = 'all';
const FILE_TYPES = Object.freeze(new Set([FILE_TYPE, FILE_DIR_TYPE, ALL_TYPE]));
const DIR_TYPES = Object.freeze(new Set([DIR_TYPE, FILE_DIR_TYPE, ALL_TYPE]));
const READ_OPTIONS = {encoding: 'utf8'};

class ReaddirpStream extends Readable {
  static get defaultOptions() {
    return {
      root: '.',
      fileFilter: (path) => true,
      directoryFilter: (path) => true,
      entryType: 'files',
      lstat: false,
      depth: 2147483648
    }
  }

  constructor(options = {}) {
    super({objectMode: true, highWaterMark: 1});
    const opts = {...ReaddirpStream.defaultOptions, ...options};
    const {root} = opts;
    if (root == null || typeof root === 'undefined') {
      this._handleFatalError(new Error('readdirp: root argument is required. Usage: readdirp(root, options)'));
    } else if (typeof root !== 'string') {
      this._handleFatalError(new Error(`readdirp: root argument must be a string. Usage: readdirp(root, options)`));
    }

    this._fileFilter = normalizeFilter(opts.fileFilter);
    this._directoryFilter = normalizeFilter(opts.directoryFilter);
    this._stat = opts.lstat ? lstat : stat;
    this._maxDepth = opts.depth;
    this._entryType = opts.entryType;
    this._root = root;

    // Launch stream with one parent, the root dir.
    this.parents = [{parentPath: root, depth: 0}];
    this.filesToRead = 0;
  }

  async _read() {
    // If the stream was destroyed, we must not proceed.
    if (!this.readable) return;

    const parent = this.parents.pop();

    // All directories have been read...
    if (!parent) {
      // ...stop stream when there are no files to process.
      this._endStreamIfQueueIsEmpty();

      // ...we have files to process; but not directories.
      // hence, parent is undefined; and we cannot execute fs.readdir().
      // The files are being processed anywhere.
      return;
    }

    const {parentPath, depth} = parent;

    // To prevent race conditions, we increase counter while awaiting readdir.
    this.filesToRead++;
    const files = await readdir(parentPath, READ_OPTIONS).catch(e => {
      if (e.code === NO_ENTRY_ERROR) {
        return [];
      }
      throw e;
    });
    this.filesToRead--;

    // If the stream was destroyed, after readdir is completed
    if (!this.readable) return;

    this.filesToRead += files.length;

    for (const relativePath of files) {
      const fullPath = sysPath.resolve(sysPath.join(parentPath, relativePath));
      const stat = await this._stat(fullPath);
      const path = sysPath.relative(this._root, fullPath);
      const basename = sysPath.basename(path);
      const entry = {path, stat, fullPath, basename, root: this._root};

      if (this._isDirAndMatchesFilter(entry)) {
        this._pushNewParentIfLessThanMaxDepth(fullPath, depth + 1);
        this._emitPushIfUserWantsDir(entry);
        if (!this.isPaused()) this._read();
      } else if (this._isFileAndMatchesFilter(entry)) {
        this._emitPushIfUserWantsFile(entry);
      }
      this.filesToRead--;
    }

    this._endStreamIfQueueIsEmpty(parentPath);
  }

  _endStreamIfQueueIsEmpty(pp) {
    if (this.parents.length === 0 && this.filesToRead === 0 && this.readable) {
      this.push(null);
    }
  }

  _pushNewParentIfLessThanMaxDepth(parentPath, depth) {
    if (depth <= this._maxDepth) {
      this.parents.push({parentPath, depth});
      return true
    } else {
      return false;
    }
  }

  _isDirAndMatchesFilter(entry) {
    return entry.stat.isDirectory() && this._directoryFilter(entry);
  }

  _isFileAndMatchesFilter(entry) {
    const {stat} = entry;
    const isFileType = (
      (this._entryType === ALL_TYPE && !stat.isDirectory()) ||
      (stat.isFile() || stat.isSymbolicLink())
    );
    return isFileType && this._fileFilter(entry);
  }

  _emitPushIfUserWantsDir(entry) {
    if (DIR_TYPES.has(this._entryType)) {
      this._push(entry);
    }
  }

  _emitPushIfUserWantsFile(entry) {
    if (FILE_TYPES.has(this._entryType)) {
      this._push(entry);
    }
  }

  _push(entry) {
    if (this.readable) {
      this.push(entry);
    }
  }

  _handleError(error) {
    if (this.readable) {
      this.emit('warn', error);
    }
  }

  _handleFatalError(error) {
    this.destroy();
    this.emit('error', error);
  }

  destroy() {
    this.emit('close');
  }
}

/**
 * @typedef {Object} ReaddirpArguments
 * @property {Function=} fileFilter
 * @property {Function=} directoryFilter
 * @property {String=} entryType
 * @property {Number=} depth
 * @property {String=} root
 * @property {Boolean=} lstat
 */

/**
 * Main function which ends up calling readdirRec and reads all files and directories in given root recursively.
 * @param {String} root Root directory
 * @param {ReaddirpArguments=} options Options to specify root (start directory), filters and recursion depth
 */
const readdirp = (root, options = {}) => {
  let error;
  if (!error) options.root = root;
  const stream = new ReaddirpStream(options);
  if (error) stream._handleFatalError(error);
  return stream;
};

const readdirpPromise = (root, options = {}) => {
  return new Promise((resolve, reject) => {
    const files = [];
    readdirp(root, options)
      .on('data', (entry) => { files.push(entry); })
      .on('end', () => { resolve(files); })
      .on('error', (error) => { reject(error); });
  });
};

readdirp.promise = readdirpPromise;
readdirp.ReaddirpStream = ReaddirpStream;

module.exports = readdirp;
<commit_msg>fix(lstat): Doesn't trigger file if it will unlinked when stat was in pending.<commit_after>'use strict';

const fs = require('fs');
const {Readable} = require('stream');
const sysPath = require('path');
const {promisify} = require('util');
const picomatch = require('picomatch');
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);
const lstat = promisify(fs.lstat);

const BANG = '!';
const normalizeFilter = (filter) => {
  if (filter === undefined) return;
  if (typeof filter === "function") return filter;

  if (typeof filter === "string") {
    const glob = picomatch(filter.trim());
    return (entry) => glob(entry.basename);
  }

  if (Array.isArray(filter)) {
    const positive = [];
    const negative = [];
    for (const item of filter) {
      const trimmed = item.trim();
      if (trimmed.charAt(0) === BANG) {
        negative.push(picomatch(trimmed.slice(1)));
      } else {
        positive.push(picomatch(trimmed));
      }
    }

    if (negative.length) {
      if (positive.length) {
        return (entry) => positive.some(f => f(entry.basename)) &&
          !negative.every(f => f(entry.basename));
      } else {
        return (entry) => !negative.every(f => f(entry.basename));
      }
    } else {
      return (entry) => positive.some(f => f(entry.basename));
    }
  }
};

const NO_ENTRY_ERROR = 'ENOENT';
const FILE_TYPE = 'files';
const DIR_TYPE = 'directories';
const FILE_DIR_TYPE = 'both';
const ALL_TYPE = 'all';
const FILE_TYPES = Object.freeze(new Set([FILE_TYPE, FILE_DIR_TYPE, ALL_TYPE]));
const DIR_TYPES = Object.freeze(new Set([DIR_TYPE, FILE_DIR_TYPE, ALL_TYPE]));
const READ_OPTIONS = {encoding: 'utf8'};

const handleNoEntryError = value => e => {
  if (e.code === NO_ENTRY_ERROR) {
    return value;
  }
  throw e;
};

class ReaddirpStream extends Readable {
  static get defaultOptions() {
    return {
      root: '.',
      fileFilter: (path) => true,
      directoryFilter: (path) => true,
      entryType: 'files',
      lstat: false,
      depth: 2147483648
    }
  }

  constructor(options = {}) {
    super({objectMode: true, highWaterMark: 1});
    const opts = {...ReaddirpStream.defaultOptions, ...options};
    const {root} = opts;
    if (root == null || typeof root === 'undefined') {
      this._handleFatalError(new Error('readdirp: root argument is required. Usage: readdirp(root, options)'));
    } else if (typeof root !== 'string') {
      this._handleFatalError(new Error(`readdirp: root argument must be a string. Usage: readdirp(root, options)`));
    }

    this._fileFilter = normalizeFilter(opts.fileFilter);
    this._directoryFilter = normalizeFilter(opts.directoryFilter);
    this._stat = opts.lstat ? lstat : stat;
    this._maxDepth = opts.depth;
    this._entryType = opts.entryType;
    this._root = root;

    // Launch stream with one parent, the root dir.
    this.parents = [{parentPath: root, depth: 0}];
    this.filesToRead = 0;
  }

  async _read() {
    // If the stream was destroyed, we must not proceed.
    if (!this.readable) return;

    const parent = this.parents.pop();

    // All directories have been read...
    if (!parent) {
      // ...stop stream when there are no files to process.
      this._endStreamIfQueueIsEmpty();

      // ...we have files to process; but not directories.
      // hence, parent is undefined; and we cannot execute fs.readdir().
      // The files are being processed anywhere.
      return;
    }

    const {parentPath, depth} = parent;

    // To prevent race conditions, we increase counter while awaiting readdir.
    this.filesToRead++;
    const files = await readdir(parentPath, READ_OPTIONS).catch(handleNoEntryError([]));
    this.filesToRead--;

    // If the stream was destroyed, after readdir is completed
    if (!this.readable) return;

    this.filesToRead += files.length;

    for (const relativePath of files) {
      const fullPath = sysPath.resolve(sysPath.join(parentPath, relativePath));
      const stat = await this._stat(fullPath).catch(handleNoEntryError());
      if (!stat) {
        this.filesToRead--;
        continue;
      }
      const path = sysPath.relative(this._root, fullPath);
      const basename = sysPath.basename(path);
      const entry = {path, stat, fullPath, basename, root: this._root};

      if (this._isDirAndMatchesFilter(entry)) {
        this._pushNewParentIfLessThanMaxDepth(fullPath, depth + 1);
        this._emitPushIfUserWantsDir(entry);
        if (!this.isPaused()) this._read();
      } else if (this._isFileAndMatchesFilter(entry)) {
        this._emitPushIfUserWantsFile(entry);
      }
      this.filesToRead--;
    }

    this._endStreamIfQueueIsEmpty(parentPath);
  }

  _endStreamIfQueueIsEmpty(pp) {
    if (this.parents.length === 0 && this.filesToRead === 0 && this.readable) {
      this.push(null);
    }
  }

  _pushNewParentIfLessThanMaxDepth(parentPath, depth) {
    if (depth <= this._maxDepth) {
      this.parents.push({parentPath, depth});
      return true
    } else {
      return false;
    }
  }

  _isDirAndMatchesFilter(entry) {
    return entry.stat.isDirectory() && this._directoryFilter(entry);
  }

  _isFileAndMatchesFilter(entry) {
    const {stat} = entry;
    const isFileType = (
      (this._entryType === ALL_TYPE && !stat.isDirectory()) ||
      (stat.isFile() || stat.isSymbolicLink())
    );
    return isFileType && this._fileFilter(entry);
  }

  _emitPushIfUserWantsDir(entry) {
    if (DIR_TYPES.has(this._entryType)) {
      this._push(entry);
    }
  }

  _emitPushIfUserWantsFile(entry) {
    if (FILE_TYPES.has(this._entryType)) {
      this._push(entry);
    }
  }

  _push(entry) {
    if (this.readable) {
      this.push(entry);
    }
  }

  _handleError(error) {
    if (this.readable) {
      this.emit('warn', error);
    }
  }

  _handleFatalError(error) {
    this.destroy();
    this.emit('error', error);
  }

  destroy() {
    this.emit('close');
  }
}

/**
 * @typedef {Object} ReaddirpArguments
 * @property {Function=} fileFilter
 * @property {Function=} directoryFilter
 * @property {String=} entryType
 * @property {Number=} depth
 * @property {String=} root
 * @property {Boolean=} lstat
 */

/**
 * Main function which ends up calling readdirRec and reads all files and directories in given root recursively.
 * @param {String} root Root directory
 * @param {ReaddirpArguments=} options Options to specify root (start directory), filters and recursion depth
 */
const readdirp = (root, options = {}) => {
  let error;
  if (!error) options.root = root;
  const stream = new ReaddirpStream(options);
  if (error) stream._handleFatalError(error);
  return stream;
};

const readdirpPromise = (root, options = {}) => {
  return new Promise((resolve, reject) => {
    const files = [];
    readdirp(root, options)
      .on('data', (entry) => { files.push(entry); })
      .on('end', () => { resolve(files); })
      .on('error', (error) => { reject(error); });
  });
};

readdirp.promise = readdirpPromise;
readdirp.ReaddirpStream = ReaddirpStream;

module.exports = readdirp;
<|endoftext|>


<commit_before>var slice = [].slice, path = require('path'), fs = require('fs');

function die () {
  console.log.apply(console, slice.call(arguments, 0));
  process.exit(1);
}

function say () {
  console.log.apply(console, slice.call(arguments, 0));
}

// Create a regular expression that matches a specific string.
function regular (text) { return text.replace(REGEX, '\\$1') }

var REGEX = new RegExp('(\\' + '/ . * + ? | ( ) [ ] { } \\'.split(' ').join('|\\') + ')', 'g');

// Exract a usage message from a file.
function extractUsage (lang, source, argv) {
  var lines = fs.readFileSync(source, 'utf8').split(/\r?\n/),
      i, j, I, line, indent, $, candidate, _default, usage,
      message, command, match, langs;

  // **TODO**: Note that the test to see if we matched a line is if there is no
  // sub-command specified, or else the sub-command matches the first argument.
  // This would seem to imply that the default usage string should come last,
  // but I've not written an application that has a default usage string yet.
  //
  // Need to allow the no sub-command to match, but then get vetoed by a matched
  // sub-command.
  //
  // Note that the regular expression to match a usage line matches both the
  // beginning and ending markup, so we need to check in our outer loop that
  // we've matched beginning markup, and not ending markup, which would be an
  // odd occurance, probably worth abenending about.

  //
  OUTER: for (i = 0, I = lines.length;i < I; i++) {
    if ($ = USAGE_RE.exec(lines[i])) {
      if (!$[3]) continue OUTER;  // Matched ending markup.
      langs = $[3].split(/\s+/);
      if ((!$[1] || $[1] == argv[0]) && (!_default || ~langs.indexOf(lang))) {
        command = $[1];
        indent = /^(\s*)/.exec(lines[i])[1].length;
        for (j = i + 1; j < I; j++) {
          if (/\S/.test(lines[j])) {
            indent = Math.min(indent, /^(\s*)/.exec(lines[j])[1].length);
          }
          if ($ = USAGE_RE.exec(lines[j])) {
            if (!message) {
              message = lines.slice(i + 1, j).map(function (line) { return line.substring(indent) });
              usage = { message: message };
              if (command) usage.command = command;
            } else {
              usage.errors = errors(lines.slice(i + 1, j));
            }
            if ($[2] == 'strings') {
              i = j;
              continue;
            }
            if (!_default) _default = usage;
            if (~langs.indexOf(lang)) {
              usage["default"] = _default;
              return usage;
            }
            i = j - 1;
            message = null;
            continue OUTER;
          }
        }
        return null;
      }
    }
  }

  if (_default) _default["default"] = _default;

  return _default;
}

// The regular expression to match usage markdown.
var USAGE_RE = /^\s*___(?:\s+(\w+)\s+_)?\s+(usage|strings)(?::\s+((?:[a-z]{2}_[A-Z]{2})(?:\s+[a-z]{2}_[A-Z]{2})*))?\s+___\s*/;

// Extract message strings from the errors section of a usage message.
function errors (lines) {
  var i, I, j, J, $, spaces, key, line, message = [], dedent = Number.MAX_VALUE, errors = {};

  OUTER: for (i = 0, I = lines.length; i < I; i++) {
    if (($ = /^(\s*)([^:]+):\s*(.*)$/.exec(lines[i]))) {
      spaces = $[1].length, key = $[2], line = $[3], message = [];
      if (line.length) message.push(line);
      for (i++; i < I; i++) {
        if (/\S/.test(lines[i])) {
          $ = /^(\s*)(.*)$/.exec(lines[i]);
          if ($[1].length <= spaces) break;
          dedent = Math.min($[1].length, dedent);
        }
        message.push(lines[i]);
      }
      for (j = line.length ? 1 : 0, J = message.length; j < J; j++) {
        message[j] = message[j].substring(dedent);
      }
      if (message[message.length - 1] == '') message.pop();
      errors[key] = message.join('\n');
      i--;
    }
  }

  return errors;
}

// First argument is optionally a language identifier. This is easily obtained
// from the environment and passed in as the first argument. The first required
// argument is the file used to for a usage strings, followed by arguments,
// which are flattened. And optional callback is the final argument. If provided
// the callback is invoked with the options array. Any exception thrown is
// reported. If it begins with something that looks like an error warning, then
// the stack trace is supressed and the error message is followed by usage.

function parse () {
  var vargs = slice.call(arguments, 0), lang = 'en_US',
      flags = {}, numeric = /^(count|number|value|size)$/,
      arg, arrayed = {}, pat = '', $ , main, errors, message;

  function abended (usage, message) {
    if (!usage) throw new Error("no usage message");
    if (message) console.log(message);
  }

  // Caller provisioned error handler.
  if (typeof vargs[vargs.length - 2] == 'function') abended = vargs.pop();

  // Caller provisioned main function.
  if (typeof vargs[vargs.length - 1] == 'function') main = vargs.pop();

  // Caller specified language string.
  if (($ = /^(\w{2}_\w{2})(?:\.[\w\d-]+)?$/.exec(vargs[0])) && vargs.shift()) lang = $[1];

  var source = vargs.shift();                     // File in which to look for usage.
  var argv = flatten(vargs);                      // Flatten arguments.
  var usage = extractUsage(lang, source, argv); // Extract a usage message.

  // No usage message found, report error and return `undefined`.
  if (!usage) {
    if (main) abended(null);
    return;
  }

  // Now we have a usage message, so we can begin to build our options object.

  // When invoked with a sub-command, adjust `argv`.
  if (usage.command) argv.shift();

  // Extract a definition of the command line arguments from the usage message
  // while tiding the usage message; removing special characters that are flags
  // to Arguable that do not belong in the usage message printed to `stdout`.
  usage.message = usage.message.map(function (line) {
    var verbose, terse = '-\t', type = '!', arrayed, out = '', $, trim = /^$/;
    if ($ = /^(?:[\s*@]*(-[\w\d])[@\s]*,)?[@\s]*(--\w[-\w\d_]*)(?:[\s@]*[\[<]([^\]>]+)[\]>][\s@]*)?/.exec(line)) {
      out = $[0], terse = $[1] || '-\t'
                , verbose = $[2]
                , type = $[3] && (numeric.test($[3]) ? '#' : '$') || '!'
                , line = line.substring(out.length);
      arrayed = ~out.indexOf('@') ? '@' : ':';
      pat += terse + ',' + verbose + arrayed + type + '|';
      if (!line.length) trim = /\s+$/;
    }
    return (out.replace('@', ' ') + line).replace(trim, '');
  }).join('\n');

  // Here's the legacy confusion.
  try {
    usage.given = getopt(pat, usage.params = {}, argv);
  } catch (e) {
    // TODO: I18n is missing from here.
    if (main) {
      console.error(e.message);
      console.error(usage.message);
    } else {
      e.usage = usage.message;
      throw e;
    }
  }

  // And here's the legacy bridge.
  usage.$argv = argv;
  var objectified = new Options(usage, 0);
  if (main) {
    try {
      main(objectified);
    } catch (e) {
      if (e._type === Options.prototype.help) {
        abended(objectified.usage);
      } else if (e._type === Options.prototype.abend) {
        message = objectified._errors[e.message] || objectified.defaultLanguage._errors[e.message];
        abended(objectified.usage, message, e._arguments || []);
      } else {
        throw e;
      }
    }
  }
  return objectified;
}

function Options (legacy, depth) {
  var options = this;
  options.args = legacy.$argv;
  options.params = {};
  options.usage = legacy.message;
  options.params = legacy.params;
  options.given = legacy.given;
  options._errors = legacy.errors;
  if (legacy.command) options.command = legacy.command;
  if (!depth && legacy.$defaultLanguage) options.defaultLanguage = new Options(legacy.$defaultLanguage, depth + 1);
}

Options.prototype.help = function (message) {
  var e = new Error(message);
  e._type = Options.prototype.help;
  throw e;
}

Options.prototype.abend = function (message) {
  var e = new Error(message);
  e._type = Options.prototype.abend;
  e._arguments = slice.call(arguments, 1);
  throw e;
}

function abend (message) {
  throw new Error(message);
}

// A regex rocking argument parser implementation. It can do most of the
// manipulations of GNU `getopt`, parsing long options, short options, long
// options who's value is deilmited by an equal sign, short options all mushed
// together, etc.
function getopt (pat, opts, argv) {
  var arg, i = 0, $, arg, opt, l, alts, given = {};
  pat.replace(/--([^-]+)@/, function ($1, verbose) { opts[verbose] = [] });
  while (!(i >= argv.length || (argv[i] == "--" && argv.shift()) || !/^--?[^-]/.test(argv[i]))) {
    arg = argv.shift();
    arg = /^(--[^=]+)=(.*)$/.exec(arg) || /^(-[^-])(.+)$/.exec(arg) || [false, arg, true];
    alts = pat.replace(new RegExp(regular(arg[1]), 'g'), '').replace(/-[^,],--[^|]+\|/g, '').split("|");
    if ((l = alts.length - 1) != 1) abend((l ? "ambiguous: " : "unknown option: ") + arg[1], true);
    opt = (arg[1] + /,([^:@]*)/.exec(alts[0])[1]).replace(/^(-[^-]+)?--/, '').replace(/-/g, '');
    $ = /([:@])(.)$/.exec(alts[0]);
    if ($[2] != '!') {
      if ($[1].length == 1 || (argv.length && argv[0][0] != "-")) {
        if (!arg[0]) {
          if (!argv.length) abend("missing argument for: " + (arg[1][1] != "-" ? arg[1] : "--" + opt), true);
          arg[2] = argv.shift();
        }
        if ($[2] == '#' && isNaN(arg[2] = +arg[2])) abend("numeric option: " + arg[1]);
      }
    } else if (arg[0]) {
      if (arg[1][1] != "-") {
        argv.unshift("-" + arg[2]);
      } else {
        abend("option does not take value: " + (arg[1][1] != "-" ? arg[1] : "--" + opt), true);
      }
    }
    given[opt] = true;
    if ($[1] == '@') opts[opt].push(arg[2]);
    else if (opts[opt] != null) abend("option can only be secified once: " + arg[1]);
    else opts[opt] = arg[2];
  }
  return Object.keys(given);
}

// Flatten an object or an array or an arry of objects or what have you into
// long option command line parameters. This is one of the resasons we require
// that usage definitions define a long option for each parameter.
function flatten () {
  var flattened = [];
  slice.call(arguments, 0).forEach(function (arg) {
    if (arg != null) {
      if (Array.isArray(arg)) {
        flattened.push.apply(flattened, flatten.apply(this, arg));
      } else if (typeof arg == "object") {
        Object.keys(arg).forEach(function (key) {
          (Array.isArray(arg[key]) ? arg[key] : [ arg[key] ]).forEach(function (value) {
            flattened.push('--' + key);
            if (typeof value != 'boolean') flattened.push(value);
          });
        });
      } else {
        flattened.push(arg);
      }
    }
  });
  return flattened;
}

module.exports.parse = parse;
module.exports.flatten = flatten;
<commit_msg>Complete conversion to `Options` object.<commit_after>var slice = [].slice, path = require('path'), fs = require('fs');

function die () {
  console.log.apply(console, slice.call(arguments, 0));
  process.exit(1);
}

function say () {
  console.log.apply(console, slice.call(arguments, 0));
}

// Create a regular expression that matches a specific string.
function regular (text) { return text.replace(REGEX, '\\$1') }

var REGEX = new RegExp('(\\' + '/ . * + ? | ( ) [ ] { } \\'.split(' ').join('|\\') + ')', 'g');

// Exract a usage message from a file.
function extractUsage (lang, source, argv) {
  var lines = fs.readFileSync(source, 'utf8').split(/\r?\n/),
      i, j, I, line, indent, $, candidate, _default, usage,
      message, command, match, langs;

  // **TODO**: Note that the test to see if we matched a line is if there is no
  // sub-command specified, or else the sub-command matches the first argument.
  // This would seem to imply that the default usage string should come last,
  // but I've not written an application that has a default usage string yet.
  //
  // Need to allow the no sub-command to match, but then get vetoed by a matched
  // sub-command.
  //
  // Note that the regular expression to match a usage line matches both the
  // beginning and ending markup, so we need to check in our outer loop that
  // we've matched beginning markup, and not ending markup, which would be an
  // odd occurance, probably worth abenending about.

  //
  OUTER: for (i = 0, I = lines.length;i < I; i++) {
    if ($ = USAGE_RE.exec(lines[i])) {
      if (!$[3]) continue OUTER;  // Matched ending markup.
      langs = $[3].split(/\s+/);
      if ((!$[1] || $[1] == argv[0]) && (!_default || ~langs.indexOf(lang))) {
        command = $[1];
        indent = /^(\s*)/.exec(lines[i])[1].length;
        for (j = i + 1; j < I; j++) {
          if (/\S/.test(lines[j])) {
            indent = Math.min(indent, /^(\s*)/.exec(lines[j])[1].length);
          }
          if ($ = USAGE_RE.exec(lines[j])) {
            if (!message) {
              message = lines.slice(i + 1, j).map(function (line) { return line.substring(indent) });
              usage = { message: message };
              if (command) usage.command = command;
            } else {
              usage.errors = errors(lines.slice(i + 1, j));
            }
            if ($[2] == 'strings') {
              i = j;
              continue;
            }
            if (!_default) _default = usage;
            if (~langs.indexOf(lang)) {
              usage["default"] = _default;
              return usage;
            }
            i = j - 1;
            message = null;
            continue OUTER;
          }
        }
        return null;
      }
    }
  }

  if (_default) _default["default"] = _default;

  return _default;
}

// The regular expression to match usage markdown.
var USAGE_RE = /^\s*___(?:\s+(\w+)\s+_)?\s+(usage|strings)(?::\s+((?:[a-z]{2}_[A-Z]{2})(?:\s+[a-z]{2}_[A-Z]{2})*))?\s+___\s*/;

// Extract message strings from the errors section of a usage message.
function errors (lines) {
  var i, I, j, J, $, spaces, key, line, message = [], dedent = Number.MAX_VALUE, errors = {};

  OUTER: for (i = 0, I = lines.length; i < I; i++) {
    if (($ = /^(\s*)([^:]+):\s*(.*)$/.exec(lines[i]))) {
      spaces = $[1].length, key = $[2], line = $[3], message = [];
      if (line.length) message.push(line);
      for (i++; i < I; i++) {
        if (/\S/.test(lines[i])) {
          $ = /^(\s*)(.*)$/.exec(lines[i]);
          if ($[1].length <= spaces) break;
          dedent = Math.min($[1].length, dedent);
        }
        message.push(lines[i]);
      }
      for (j = line.length ? 1 : 0, J = message.length; j < J; j++) {
        message[j] = message[j].substring(dedent);
      }
      if (message[message.length - 1] == '') message.pop();
      errors[key] = message.join('\n');
      i--;
    }
  }

  return errors;
}

// First argument is optionally a language identifier. This is easily obtained
// from the environment and passed in as the first argument. The first required
// argument is the file used to for a usage strings, followed by arguments,
// which are flattened. And optional callback is the final argument. If provided
// the callback is invoked with the options array. Any exception thrown is
// reported. If it begins with something that looks like an error warning, then
// the stack trace is supressed and the error message is followed by usage.

function parse () {
  var vargs = slice.call(arguments, 0), lang = 'en_US',
      flags = {}, numeric = /^(count|number|value|size)$/,
      arg, arrayed = {}, pat = '', $ , main, errors, message;

  function abended (usage, message) {
    if (!usage) throw new Error("no usage message");
    if (message) console.log(message);
  }

  // Caller provisioned error handler.
  if (typeof vargs[vargs.length - 2] == 'function') abended = vargs.pop();

  // Caller provisioned main function.
  if (typeof vargs[vargs.length - 1] == 'function') main = vargs.pop();

  // Caller specified language string.
  if (($ = /^(\w{2}_\w{2})(?:\.[\w\d-]+)?$/.exec(vargs[0])) && vargs.shift()) lang = $[1];

  var source = vargs.shift();                     // File in which to look for usage.
  var argv = flatten(vargs);                      // Flatten arguments.
  var usage = extractUsage(lang, source, argv); // Extract a usage message.

  // No usage message found, report error and return `undefined`.
  if (!usage) {
    if (main) abended(null);
    return;
  }

  // Now we have a usage message, so we can begin to build our options object.

  // When invoked with a sub-command, adjust `argv`.
  if (usage.command) argv.shift();

  // Extract a definition of the command line arguments from the usage message
  // while tiding the usage message; removing special characters that are flags
  // to Arguable that do not belong in the usage message printed to `stdout`.
  usage.message = usage.message.map(function (line) {
    var verbose, terse = '-\t', type = '!', arrayed, out = '', $, trim = /^$/;
    if ($ = /^(?:[\s*@]*(-[\w\d])[@\s]*,)?[@\s]*(--\w[-\w\d_]*)(?:[\s@]*[\[<]([^\]>]+)[\]>][\s@]*)?/.exec(line)) {
      out = $[0], terse = $[1] || '-\t'
                , verbose = $[2]
                , type = $[3] && (numeric.test($[3]) ? '#' : '$') || '!'
                , line = line.substring(out.length);
      arrayed = ~out.indexOf('@') ? '@' : ':';
      pat += terse + ',' + verbose + arrayed + type + '|';
      if (!line.length) trim = /\s+$/;
    }
    return (out.replace('@', ' ') + line).replace(trim, '');
  }).join('\n');

  var options = new Options(usage);
  // Here's the legacy confusion.
  try {
    options.given = getopt(pat, options.params = {}, argv);
  } catch (e) {
    // TODO: I18n is missing from here.
    if (main) {
      console.error(e.message);
      console.error(usage.message);
    } else {
      e.usage = usage.message;
      throw e;
    }
  }

  // And here's the legacy bridge.
  options.argv = argv;
  if (main) {
    try {
      main(options);
    } catch (e) {
      if (e._type === Options.prototype.help) {
        abended(options._usage.message);
      } else if (e._type === Options.prototype.abend) {
        message = options._usage.errors[e.message] || options._usage["default"].errors[e.message];
        abended(options.usage, message, e._arguments || []);
      } else {
        throw e;
      }
    }
  }
  return options;
}

function Options (usage, command) {
  this._usage = usage;
  this.usage = usage.message;
  if (usage.command) this.command = usage.command;
}

Options.prototype.help = function (message) {
  var e = new Error(message);
  e._type = Options.prototype.help;
  throw e;
}

Options.prototype.abend = function (message) {
  var e = new Error(message);
  e._type = Options.prototype.abend;
  e._arguments = slice.call(arguments, 1);
  throw e;
}

function abend (message) {
  throw new Error(message);
}

// A regex rocking argument parser implementation. It can do most of the
// manipulations of GNU `getopt`, parsing long options, short options, long
// options who's value is deilmited by an equal sign, short options all mushed
// together, etc.
function getopt (pat, opts, argv) {
  var arg, i = 0, $, arg, opt, l, alts, given = {};
  pat.replace(/--([^-]+)@/, function ($1, verbose) { opts[verbose] = [] });
  while (!(i >= argv.length || (argv[i] == "--" && argv.shift()) || !/^--?[^-]/.test(argv[i]))) {
    arg = argv.shift();
    arg = /^(--[^=]+)=(.*)$/.exec(arg) || /^(-[^-])(.+)$/.exec(arg) || [false, arg, true];
    alts = pat.replace(new RegExp(regular(arg[1]), 'g'), '').replace(/-[^,],--[^|]+\|/g, '').split("|");
    if ((l = alts.length - 1) != 1) abend((l ? "ambiguous: " : "unknown option: ") + arg[1], true);
    opt = (arg[1] + /,([^:@]*)/.exec(alts[0])[1]).replace(/^(-[^-]+)?--/, '').replace(/-/g, '');
    $ = /([:@])(.)$/.exec(alts[0]);
    if ($[2] != '!') {
      if ($[1].length == 1 || (argv.length && argv[0][0] != "-")) {
        if (!arg[0]) {
          if (!argv.length) abend("missing argument for: " + (arg[1][1] != "-" ? arg[1] : "--" + opt), true);
          arg[2] = argv.shift();
        }
        if ($[2] == '#' && isNaN(arg[2] = +arg[2])) abend("numeric option: " + arg[1]);
      }
    } else if (arg[0]) {
      if (arg[1][1] != "-") {
        argv.unshift("-" + arg[2]);
      } else {
        abend("option does not take value: " + (arg[1][1] != "-" ? arg[1] : "--" + opt), true);
      }
    }
    given[opt] = true;
    if ($[1] == '@') opts[opt].push(arg[2]);
    else if (opts[opt] != null) abend("option can only be secified once: " + arg[1]);
    else opts[opt] = arg[2];
  }
  return Object.keys(given);
}

// Flatten an object or an array or an arry of objects or what have you into
// long option command line parameters. This is one of the resasons we require
// that usage definitions define a long option for each parameter.
function flatten () {
  var flattened = [];
  slice.call(arguments, 0).forEach(function (arg) {
    if (arg != null) {
      if (Array.isArray(arg)) {
        flattened.push.apply(flattened, flatten.apply(this, arg));
      } else if (typeof arg == "object") {
        Object.keys(arg).forEach(function (key) {
          (Array.isArray(arg[key]) ? arg[key] : [ arg[key] ]).forEach(function (value) {
            flattened.push('--' + key);
            if (typeof value != 'boolean') flattened.push(value);
          });
        });
      } else {
        flattened.push(arg);
      }
    }
  });
  return flattened;
}

module.exports.parse = parse;
module.exports.flatten = flatten;
<|endoftext|>


<commit_before>var pull = require('pull-core')

function destroy(stream, cb) {
  function onClose () {
    cleanup(); cb()
  }
  function onError (err) {
    cleanup(); cb(err)
  }
  function cleanup() {
    stream.removeListener('close', onClose)
    stream.removeListener('error', onError)
  }
  stream.on('close', onClose)
  stream.on('error', onError)
}

function write(read, stream) {
  var ended
  function onClose () {
    cleanup()
    if(!ended) read(ended = true, function () {})
  }
  function onError (err) {
    cleanup()
    if(!ended) read(ended = err, function () {})
  }
  function cleanup() {
    stream.removeListener('close', onClose)
    stream.removeListener('error', onError)
  }
  stream.on('close', onClose)
  stream.on('error', onError)
  process.nextTick(function next() {
    read(null, function (end, data) {
      if(end === true)
        return stream._isStdio || stream.end()
      if(ended = ended || end)
        return stream.emit('error', end)

      var pause = stream.write(data)
      if(pause === false)
        stream.once('drain', next)
      else next()
    })
  })
}

function first (emitter, events, handler) {
  function listener (val) {
    events.forEach(function (e) {
      emitter.removeListener(e, listener)
    })
    handler(val)
  }
  events.forEach(function (e) {
    emitter.on(e, listener)
  })
  return emitter
}

function read2(stream) {
  var ended = false, waiting = false
  var _cb

  function read () {
    var data = stream.read()
    if(data !== null && _cb) {
      var cb = _cb; _cb = null
      cb(null, data)
    }
  }

  stream.on('readable', function () {
    waiting = true
    _cb && read()
  })
  .on('end', function () {
    ended = true
    _cb && _cb(ended)
  })
  .on('error', function (err) {
    ended = err
    _cb && _cb(ended)
  })

  return function (end, cb) {
    _cb = cb
    if(ended)
      cb(ended)
    else if(waiting)
      read()
    return
    ;(function next () {
      if(ended && ended !== true) //ERROR
        return cb(ended)
      var data = stream.read()
      if(data == null) {
        if(ended)
          return cb(ended)
        _cb = cb
        stream.on('readable', next)
      } else {
        return cb(null, data)
      }
    })()
  }
}

function read(stream) {
  if('function' === typeof stream.read)
    return read2(stream)

  var buffer = [], cbs = [], ended, paused = false

  var draining
  function drain() {
    while((buffer.length || ended) && cbs.length)
      cbs.shift()(buffer.length ? null : ended, buffer.shift())
    if(!buffer.length && (paused)) {
      paused = false
      stream.resume()
    }
  }

  stream.on('data', function (data) {
    buffer.push(data)
    drain()
    if(buffer.length && stream.pause) {
      paused = true
      stream.pause()
    }
  })
  stream.on('end', function () {
    ended = true
    drain()
  })
  stream.on('error', function (err) {
    ended = err
    drain()
  })
  return function (abort, cb) {
    if(!cb) throw new Error('*must* provide cb')
    if(abort) {
      stream.once('close', function () {
        cb(abort)
      })
      stream.destroy()
    }
    cbs.push(cb)
    drain()
  }
}

var sink = function (stream) {
  return pull.Sink(function (read) {
    return write(read, stream)
  })()
}

var source = function (stream) {
  return pull.Source(function () { return read(stream) })()
}

exports = module.exports = function (stream) {
  return (
    stream.writable
    ? stream.readable
      ? pull.Through(function(_read) {
          write(_read, stream);
          return read(stream)
        })()
      : sink(stream)
    : source(stream)
  )
}

exports.sink = sink
exports.source = source
<commit_msg>make it possible to force stream1 reading of streams, since streams2 are weird<commit_after>var pull = require('pull-core')

function destroy(stream, cb) {
  function onClose () {
    cleanup(); cb()
  }
  function onError (err) {
    cleanup(); cb(err)
  }
  function cleanup() {
    stream.removeListener('close', onClose)
    stream.removeListener('error', onError)
  }
  stream.on('close', onClose)
  stream.on('error', onError)
}

function write(read, stream) {
  var ended
  function onClose () {
    cleanup()
    if(!ended) read(ended = true, function () {})
  }
  function onError (err) {
    cleanup()
    if(!ended) read(ended = err, function () {})
  }
  function cleanup() {
    stream.removeListener('close', onClose)
    stream.removeListener('error', onError)
  }
  stream.on('close', onClose)
  stream.on('error', onError)
  process.nextTick(function next() {
    read(null, function (end, data) {
      if(end === true)
        return stream._isStdio || stream.end()
      if(ended = ended || end)
        return stream.emit('error', end)

      var pause = stream.write(data)
      if(pause === false)
        stream.once('drain', next)
      else next()
    })
  })
}

function first (emitter, events, handler) {
  function listener (val) {
    events.forEach(function (e) {
      emitter.removeListener(e, listener)
    })
    handler(val)
  }
  events.forEach(function (e) {
    emitter.on(e, listener)
  })
  return emitter
}

function read2(stream) {
  var ended = false, waiting = false
  var _cb

  function read () {
    var data = stream.read()
    if(data !== null && _cb) {
      var cb = _cb; _cb = null
      cb(null, data)
    }
  }

  stream.on('readable', function () {
    waiting = true
    _cb && read()
  })
  .on('end', function () {
    ended = true
    _cb && _cb(ended)
  })
  .on('error', function (err) {
    ended = err
    _cb && _cb(ended)
  })

  return function (end, cb) {
    console.error('read!')
    _cb = cb
    if(ended)
      cb(ended)
    else if(waiting)
      read()
    return

//    ;(function next () {
//      if(ended && ended !== true) //ERROR
//        return cb(ended)
//      var data = stream.read()
//      if(data == null) {
//        if(ended)
//          return cb(ended)
//        _cb = cb
//        stream.on('readable', next)
//      } else {
//        return cb(null, data)
//      }
//    })()
  }
}

function read1(stream) {
  var buffer = [], cbs = [], ended, paused = false

  var draining
  function drain() {
    while((buffer.length || ended) && cbs.length)
      cbs.shift()(buffer.length ? null : ended, buffer.shift())
    if(!buffer.length && (paused)) {
      paused = false
      stream.resume()
    }
  }

  stream.on('data', function (data) {
    buffer.push(data)
    drain()
    if(buffer.length && stream.pause) {
      paused = true
      stream.pause()
    }
  })
  stream.on('end', function () {
    ended = true
    drain()
  })
  stream.on('error', function (err) {
    ended = err
    drain()
  })
  return function (abort, cb) {
    if(!cb) throw new Error('*must* provide cb')
    if(abort) {
      stream.once('close', function () {
        cb(abort)
      })
      stream.destroy()
    }
    cbs.push(cb)
    drain()
  }
}

function read (stream) {
  if('function' === typeof stream.read)
    return read2(stream)
  return read1(stream)
}

var sink = function (stream) {
  return pull.Sink(function (read) {
    return write(read, stream)
  })()
}

var source = function (stream) {
  return pull.Source(function () { return read(stream) })()
}

exports = module.exports = function (stream) {
  return (
    stream.writable
    ? stream.readable
      ? pull.Through(function(_read) {
          write(_read, stream);
          return read(stream)
        })()
      : sink(stream)
    : source(stream)
  )
}

exports.sink = sink
exports.source = source
exports.read = read
exports.read1 = read1
exports.read2 = read2

<|endoftext|>


<commit_before>//  Thing in curly braces is called meta, ex git log message
//  "foo {bar:1} {baz:2}" has two meta's and "foo {bar:1;baz:2}" has a
//  compound meta. Individual metas in compound meta affects each over, ex
//  'amend' will amend only key-values from compound meta it is in.
//
//  The only implementation challenge is 'amend' logic. What to do if
//  specified commit does not have a meta with specified key, or does not
//  have any meta at all? If we want an ability to add something via
//  'amend' AND an ability to keep a sequence order of commits (and we
//  want in order to track releases) when we need to keep a list of all
//  commits with attached metas instead of just list of metas.

const spawn = require('child_process').spawn;
const Promise = require('promise');

module.exports = function(...args) {
  const {options, next} = getOptions(args);
  const promised = new Promise((resolve, reject) => {
    return Promise.all([
      getLogTextAsync(),
      getTagTextAsync(),
      getTagHashesAsync(),
    ]).then(([log, tags, tagsToHashes]) => {
      return new Promise.resolve([])
        .then((v) => addLogToCommits(v, log))
        .then((v) => addTagsToCommits(v, tags, tagsToHashes))
        //  Git displays commits from last to first.
        .then((v) => readMetaAsync(v.reverse()))
        .then((v) => applyAmendAsync(v))
        .then((v) => metaToJsonAsync(v))
        .then((v) => resolve(JSON.stringify(v)))
    }).catch((v) => reject(v));
  });
  if (next) return promised.then((v) => next(null, v), (v) => next(v));
  return promised;
}
module.exports.debug = {};

function getOptions(args) {
  let first = args.shift() || {};
  if (typeof(first) === 'function') return {options: {}, next: first};
  return {options: first, next: args.shift()};
}

function getLogTextAsync() {
  return new Promise((resolve, reject) => {
    const stdio = ['ignore', 'pipe', process.stderr];
    const task = spawn('git', ['log', '--format=raw'], {stdio});
    let data = '';
    task.stdout.on('data', (v) => data += v);
    task.on('error', (v) => reject(v));
    task.on('close', (code) => {
      if (code !== 0) return reject(`git log failed with code ${code}`);
      resolve(data);
    });
  });
}

function getTagTextAsync() {
  return new Promise((resolve, reject) => {
    const stdio = ['ignore', 'pipe', process.stderr];
    const task = spawn('git', ['tag', '-n999'], {stdio});
    let data = '';
    task.stdout.on('data', (v) => data += v);
    task.on('error', (v) => reject(v));
    task.on('close', (code) => {
      if (code !== 0) return reject(`git tag failed with code ${code}`);
      resolve(data);
    });
  });
}

function getTagHashesAsync() {
  return new Promise((resolve, reject) => {
    const stdio = ['ignore', 'pipe', process.stderr];
    const task = spawn('git', ['show-ref', '--tags', '-d'], {stdio});
    let data = '';
    task.stdout.on('data', (v) => data += v);
    task.on('error', (v) => reject(v));
    task.on('close', (code) => {
      //! Will exit(1) if no tags exist.
      if (code !== 0) return resolve('');
      resolve(data);
    });
  });
}

function addLogToCommits(commitList, text) {
  return new Promise((resolve, reject) => {

    const addToken = (array, v) => { array.push(v); return v; };
    const nextToken = (options) => addToken(commitList, new Commit(options));
    const curToken = () => commitList.slice(-1)[0] || nextToken();

    for(line of text.split(/\r*\n+/)) {
      patternmatch(line, [
        [/^commit ([a-z0-9]+)$/, (hash) => nextToken({hash})],
        [/^    (.*)$/, (line) => curToken().addMsgLine(line)],
      ]);
    }
    resolve(commitList);
  });
}

function addTagsToCommits(commitList, tags, tagsToHashes) {
  return new Promise((resolve, reject) => {

    hashForTag = {};
    for(line of tagsToHashes.split(/\r*\n+/)) {
      patternmatch(line, [
        [/^([a-z0-9]+) refs\/tags\/([^ ]+)\^\{\}$/, (hash, tag) => {
          hashForTag[tag] = hash
        }],
      ]);
    }

    curCommit = null;
    for(line of tags.split(/\r*\n+/)) {
      patternmatch(line, [
        [/^([^ ]+)\s+(.*)$/, (tag, msg) => {
          if (!curCommit) {
            const hash = hashForTag[tag];
            curCommit = commitList.filter((v) => v.hash === hash)[0];
          }
          if (curCommit) curCommit.msg += "\n" + msg;
        }],
        [/^\s+(.*)$/, (msg) => {
          if (curCommit) curCommit.msg += "\n" + msg;
        }],
      ]);
    }

    resolve(commitList);
  });
}

module.exports.debug.readMetaAsync = readMetaAsync;
function readMetaAsync(commitList) {
  return new Promise((resolve, reject) => {

    let seq = 0;
    let prevState = 0;

    const S = {
      IDLE: 1,
      TOKEN: 2,
      SINGLE: 3,
      DOUBLE: 4,
      ESCAPE: 5,
    }

    let state = S.IDLE;
    S.STRING = [S.SINGLE, S.DOUBLE];
    for (const commit of commitList) {
      seq ++;

      const addToken = (array, v) => { array.push(v); return v; };
      const nextToken = (v) => addToken(commit.metaList, new Meta(v));
      const curToken = () => commit.metaList.slice(-1)[0] || nextToken();
      const pushState = (v) => { prevState = state; state = v; };
      const popState = () => state = prevState;

      for (var i = 0; i < commit.msg.length; i ++) {
        const hash = commit.hash;
        patternmatch([commit.msg[i], state], [
          [['{', S.IDLE], () => {
            pushState(S.TOKEN);
            nextToken({hash, seq: (seq += 1)});
          }],
          [[';', S.TOKEN], () => nextToken({hash, seq})],
          [['}', S.TOKEN], () => pushState(S.IDLE)],
          [[[':', '='], S.TOKEN], () => curToken().separate()],
          [["\'", S.TOKEN], () => pushState(S.SINGLE)],
          [["\'", S.SINGLE], () => pushState(S.TOKEN)],
          [["\"", S.TOKEN], () => pushState(S.DOUBLE)],
          [["\"", S.DOUBLE], () => pushState(S.TOKEN)],
          [["\\", S.STRING], () => pushState(S.ESCAPE)],
          [[null, S.ESCAPE], (char) => {
            curToken().add(char)
            popState();
          }],
          [[null, [S.TOKEN, S.STRING]], (char) => curToken().add(char)],
        ]);
      }
    }
    resolve(commitList);
  });
}

module.exports.debug.applyAmendAsync = applyAmendAsync;
function applyAmendAsync(commitList) {
  return new Promise((resolve, reject) => {

    for (const commit of commitList) {
      for (const meta of commit.metaList) {
        if (meta.key === 'amend') {
          for (const subMeta of commit.metaList) {
            if (subMeta.key === 'amend') continue;
            if (subMeta.seq !== meta.seq) continue;
            const hashToAmend = meta.val;
            amend(commitList, hashToAmend, subMeta.key, subMeta.val);
          }
        }
      }
    }

    resolve(commitList);
  });
}

//  In given commit list find all commit with the given hash and for all
//  metas of such commits replace val of metas with specified key.
function amend(commitList, hash, key, val) {
  for (const commit of commitList.filter((v) => v.hash === hash)) {
    let maxSeq = 0;
    let isAmended = false;
    for (const meta of commit.metaList) {
      if (meta.seq > 0) maxSeq = meta.seq;
      //  Amend by modifying existing meta.
      if (meta.key === key) {
        meta.val = val;
        isAmended = true;
      }
    }
    if (!isAmended) {
      //  Amend by adding a new meta.
      commit.metaList.push(new Meta({seq: maxSeq + 1, hash, key, val}));
    }
  }
}

function metaToJsonAsync(commitList) {
  return new Promise((resolve, reject) => {

    const releases = [];
    let curRelease = null;
    const makeNewRelease = () => curRelease = {type: 'latest', msgList: []};

    for (const commit of commitList) {
      for (const meta of commit.metaList) {
        switch (meta.key) {
          case 'msg':
            curRelease.msgList.push(meta.val);
            break;
          case 'release':
            curRelease.type = 'named';
            curRelease.name = meta.val;
            makeNewRelease();
            break;
        }
      }
    }

    resolve(releases);
  });
}

//  Pattern-matching with regexp positional matches passed to handlers.
module.exports.debug.patternmatch = patternmatch;
function patternmatch(rightSeq, pairs) {
  for (let [leftSeq, next] of pairs) {
    leftSeq = [].concat(leftSeq);
    rightSeq = [].concat(rightSeq);
    const res = [];
    const compare = (left, right) => {
      //  If test case not specified it's "don't care, match anything".
      if (left === null || left === undefined) return res.push(right), true;
      //  Test case can be an array.
      if (left.some) return left.some((v) => compare(v, right));
      if (!left[Symbol.match]) {
        if (left !== right) return false;
        return res.push(right), true;
      }
      const match = left[Symbol.match](right);
      if (!match) return false;
      return res.push(...match.slice(1)), true;
    };
    if (leftSeq.every((v, i) => compare(v, rightSeq[i]))) return next(...res);
  }
}

class Commit {
  constructor(options) {
    this.hash = '';
    this.msg = '';
    this.metaList = [];
    Object.assign(this, options);
  }
  addMsgLine(line) { this.msg += this.msg.length ? "\n" + line : line; }
}
module.exports.debug.Commit = Commit;

class Meta {
  constructor(options) {
    this.hash = '';
    this.key = '';
    this.val = '';
    //  Submetas in compound meta has same sequence number.
    this.seq = 0;
    //  ':' or '=' was found, now reading value.
    this._separated = false;
    Object.assign(this, options);
  }

  add(char) { this._separated ? this.val += char : this.key += char; }
  separate() { this._separated = true; }
}
module.exports.debug.Meta = Meta;

<commit_msg>bugfix<commit_after>//  Thing in curly braces is called meta, ex git log message
//  "foo {bar:1} {baz:2}" has two meta's and "foo {bar:1;baz:2}" has a
//  compound meta. Individual metas in compound meta affects each over, ex
//  'amend' will amend only key-values from compound meta it is in.
//
//  The only implementation challenge is 'amend' logic. What to do if
//  specified commit does not have a meta with specified key, or does not
//  have any meta at all? If we want an ability to add something via
//  'amend' AND an ability to keep a sequence order of commits (and we
//  want in order to track releases) when we need to keep a list of all
//  commits with attached metas instead of just list of metas.

const spawn = require('child_process').spawn;
const Promise = require('promise');

module.exports = function(...args) {
  const {options, next} = getOptions(args);
  const promised = new Promise((resolve, reject) => {
    return Promise.all([
      getLogTextAsync(),
      getTagTextAsync(),
      getTagHashesAsync(),
    ]).then(([log, tags, tagsToHashes]) => {
      return new Promise.resolve([])
        .then((v) => addLogToCommits(v, log))
        .then((v) => addTagsToCommits(v, tags, tagsToHashes))
        //  Git displays commits from last to first.
        .then((v) => readMetaAsync(v.reverse()))
        .then((v) => applyAmendAsync(v))
        .then((v) => metaToJsonAsync(v))
        .then((v) => resolve(JSON.stringify(v)))
    }).catch((v) => reject(v));
  });
  if (next) return promised.then((v) => next(null, v), (v) => next(v));
  return promised;
}
module.exports.debug = {};

function getOptions(args) {
  let first = args.shift() || {};
  if (typeof(first) === 'function') return {options: {}, next: first};
  return {options: first, next: args.shift()};
}

function getLogTextAsync() {
  return new Promise((resolve, reject) => {
    const stdio = ['ignore', 'pipe', process.stderr];
    const task = spawn('git', ['log', '--format=raw'], {stdio});
    let data = '';
    task.stdout.on('data', (v) => data += v);
    task.on('error', (v) => reject(v));
    task.on('close', (code) => {
      if (code !== 0) return reject(`git log failed with code ${code}`);
      resolve(data);
    });
  });
}

function getTagTextAsync() {
  return new Promise((resolve, reject) => {
    const stdio = ['ignore', 'pipe', process.stderr];
    const task = spawn('git', ['tag', '-n999'], {stdio});
    let data = '';
    task.stdout.on('data', (v) => data += v);
    task.on('error', (v) => reject(v));
    task.on('close', (code) => {
      if (code !== 0) return reject(`git tag failed with code ${code}`);
      resolve(data);
    });
  });
}

function getTagHashesAsync() {
  return new Promise((resolve, reject) => {
    const stdio = ['ignore', 'pipe', process.stderr];
    const task = spawn('git', ['show-ref', '--tags', '-d'], {stdio});
    let data = '';
    task.stdout.on('data', (v) => data += v);
    task.on('error', (v) => reject(v));
    task.on('close', (code) => {
      //! Will exit(1) if no tags exist.
      if (code !== 0) return resolve('');
      resolve(data);
    });
  });
}

function addLogToCommits(commitList, text) {
  return new Promise((resolve, reject) => {

    const addToken = (array, v) => { array.push(v); return v; };
    const nextToken = (options) => addToken(commitList, new Commit(options));
    const curToken = () => commitList.slice(-1)[0] || nextToken();

    for(line of text.split(/\r*\n+/)) {
      patternmatch(line, [
        [/^commit ([a-z0-9]+)$/, (hash) => nextToken({hash})],
        [/^    (.*)$/, (line) => curToken().addMsgLine(line)],
      ]);
    }
    resolve(commitList);
  });
}

function addTagsToCommits(commitList, tags, tagsToHashes) {
  return new Promise((resolve, reject) => {

    hashForTag = {};
    for(line of tagsToHashes.split(/\r*\n+/)) {
      patternmatch(line, [
        [/^([a-z0-9]+) refs\/tags\/([^ ]+)\^\{\}$/, (hash, tag) => {
          hashForTag[tag] = hash
        }],
      ]);
    }

    curCommit = null;
    for(line of tags.split(/\r*\n+/)) {
      patternmatch(line, [
        [/^([^ ]+)\s+(.*)$/, (tag, msg) => {
          if (!curCommit) {
            const hash = hashForTag[tag];
            curCommit = commitList.filter((v) => v.hash === hash)[0];
          }
          if (curCommit) curCommit.msg += "\n" + msg;
        }],
        [/^\s+(.*)$/, (msg) => {
          if (curCommit) curCommit.msg += "\n" + msg;
        }],
      ]);
    }

    resolve(commitList);
  });
}

module.exports.debug.readMetaAsync = readMetaAsync;
function readMetaAsync(commitList) {
  return new Promise((resolve, reject) => {

    let seq = 0;
    let prevState = 0;

    const S = {
      IDLE: 1,
      TOKEN: 2,
      SINGLE: 3,
      DOUBLE: 4,
      ESCAPE: 5,
    }

    let state = S.IDLE;
    S.STRING = [S.SINGLE, S.DOUBLE];
    for (const commit of commitList) {
      seq ++;

      const addToken = (array, v) => { array.push(v); return v; };
      const nextToken = (v) => addToken(commit.metaList, new Meta(v));
      const curToken = () => commit.metaList.slice(-1)[0] || nextToken();
      const pushState = (v) => { prevState = state; state = v; };
      const popState = () => state = prevState;

      for (var i = 0; i < commit.msg.length; i ++) {
        const hash = commit.hash;
        patternmatch([commit.msg[i], state], [
          [['{', S.IDLE], () => {
            pushState(S.TOKEN);
            nextToken({hash, seq: (seq += 1)});
          }],
          [[';', S.TOKEN], () => nextToken({hash, seq})],
          [['}', S.TOKEN], () => pushState(S.IDLE)],
          [[[':', '='], S.TOKEN], () => curToken().separate()],
          [["\'", S.TOKEN], () => pushState(S.SINGLE)],
          [["\'", S.SINGLE], () => pushState(S.TOKEN)],
          [["\"", S.TOKEN], () => pushState(S.DOUBLE)],
          [["\"", S.DOUBLE], () => pushState(S.TOKEN)],
          [["\\", S.STRING], () => pushState(S.ESCAPE)],
          [[null, S.ESCAPE], (char) => {
            curToken().add(char)
            popState();
          }],
          [[null, [S.TOKEN, S.STRING]], (char) => curToken().add(char)],
        ]);
      }
    }
    resolve(commitList);
  });
}

module.exports.debug.applyAmendAsync = applyAmendAsync;
function applyAmendAsync(commitList) {
  return new Promise((resolve, reject) => {

    for (const commit of commitList) {
      for (const meta of commit.metaList) {
        if (meta.key === 'amend') {
          for (const subMeta of commit.metaList) {
            if (subMeta.key === 'amend') continue;
            if (subMeta.seq !== meta.seq) continue;
            const hashToAmend = meta.val;
            amend(commitList, hashToAmend, subMeta.key, subMeta.val);
          }
        }
      }
    }

    resolve(commitList);
  });
}

//  In given commit list find all commit with the given hash and for all
//  metas of such commits replace val of metas with specified key.
function amend(commitList, hash, key, val) {
  for (const commit of commitList.filter((v) => v.hash === hash)) {
    let maxSeq = 0;
    let isAmended = false;
    for (const meta of commit.metaList) {
      if (meta.seq > 0) maxSeq = meta.seq;
      //  Amend by modifying existing meta.
      if (meta.key === key) {
        meta.val = val;
        isAmended = true;
      }
    }
    if (!isAmended) {
      //  Amend by adding a new meta.
      commit.metaList.push(new Meta({seq: maxSeq + 1, hash, key, val}));
    }
  }
}

function metaToJsonAsync(commitList) {
  return new Promise((resolve, reject) => {

    const releases = [];
    const makeNewRelease = () => ({type: 'latest', msgList: []});
    let curRelease = makeNewRelease();

    for (const commit of commitList) {
      for (const meta of commit.metaList) {
        switch (meta.key) {
          case 'msg':
            curRelease.msgList.push(meta.val);
            break;
          case 'release':
            curRelease.type = 'named';
            curRelease.name = meta.val;
            releases.push(curRelease);
            curRelease = makeNewRelease();
            break;
        }
      }
    }

    resolve(releases);
  });
}

//  Pattern-matching with regexp positional matches passed to handlers.
module.exports.debug.patternmatch = patternmatch;
function patternmatch(rightSeq, pairs) {
  for (let [leftSeq, next] of pairs) {
    leftSeq = [].concat(leftSeq);
    rightSeq = [].concat(rightSeq);
    const res = [];
    const compare = (left, right) => {
      //  If test case not specified it's "don't care, match anything".
      if (left === null || left === undefined) return res.push(right), true;
      //  Test case can be an array.
      if (left.some) return left.some((v) => compare(v, right));
      if (!left[Symbol.match]) {
        if (left !== right) return false;
        return res.push(right), true;
      }
      const match = left[Symbol.match](right);
      if (!match) return false;
      return res.push(...match.slice(1)), true;
    };
    if (leftSeq.every((v, i) => compare(v, rightSeq[i]))) return next(...res);
  }
}

class Commit {
  constructor(options) {
    this.hash = '';
    this.msg = '';
    this.metaList = [];
    Object.assign(this, options);
  }
  addMsgLine(line) { this.msg += this.msg.length ? "\n" + line : line; }
}
module.exports.debug.Commit = Commit;

class Meta {
  constructor(options) {
    this.hash = '';
    this.key = '';
    this.val = '';
    //  Submetas in compound meta has same sequence number.
    this.seq = 0;
    //  ':' or '=' was found, now reading value.
    this._separated = false;
    Object.assign(this, options);
  }

  add(char) { this._separated ? this.val += char : this.key += char; }
  separate() { this._separated = true; }
}
module.exports.debug.Meta = Meta;

<|endoftext|>


<commit_before>const fs = require('fs-extra');
const fileExtension = require('file-extension');

// constractor
function Upload(data){
	return function (req, res, next) {
		moveFile(req.files, data,function(err, files){
			if (err) return res.send(err);
			req.files = files;
			next();
		});
	}
}
// renaming the file
function filename(name, options){
	if (options.filename == "random") {
		return Math.random().toString(36).substring(7) + '.' + fileExtension(name);
	}else if (options.filename == "same") {
		return name.replace(fileExtension(name), '') + fileExtension(name);
	}else if (options.filename == "plus_date") {
		return name.replace('.' + fileExtension(name), '') + '_' + new Date().toString().replace(/ /g, '_') + '.' + fileExtension(name);
	} else {
		return Math.random().toString(36).substring(7) + '.' + fileExtension(name);
	}
}
// clean the file object from unwanted attributes
function restructure(file){
	return {
		path: file.path,
		type: file.type,
		size: file.size,
		name: file.name
	};
}
// moving the files
function moveFile(files, options, callback){
	let l = 1;
	// loop into the files array and move
	// the file to the the path that used want
	options.filefields.forEach(function(field) {
		if (typeof files[field] == 'undefined') {
			// check if the field is medtory or not
			if (options.used == "maybe") {
				next()
			} else if (options.used == "must") {
				return callback({code: "ExternalError", messege: "Cannot read property '" + field + "' of undefined"}, null)
			}
		}else {
		    var newPath = options.uploadDir + filename(files[field].name, options);
			fs.move(files[field]
				.path,  newPath , function (err) {
				if (err) return callback({code: "InternalError", messege: "Error happen while uploading."}, null)
				files[field].path = newPath;
				files[field] = restructure(files[field]);
				if (options.filefields.length == l) {
					clean();
				 	return callback(null, files);
				}else{
					l++;
				}
			});
		}
	});

}
// cleaning unwanted file after handling the files
function clean(){
	fs.removeSync('./upload_*')
}
module.exports = Upload;<commit_msg>remove clean file<commit_after>const fs = require('fs-extra');
const fileExtension = require('file-extension');

// constractor
function Upload(data){
	return function (req, res, next) {
		moveFile(req.files, data,function(err, files){
			if (err) return res.send(err);
			next();
		});
	}
}
// renaming the file
function filename(name, options){
	if (options.filename == "random") {
		return Math.random().toString(36).substring(7) + '.' + fileExtension(name);
	}else if (options.filename == "same") {
		return name.replace(fileExtension(name), '') + fileExtension(name);
	}else if (options.filename == "plus_date") {
		return name.replace('.' + fileExtension(name), '') + '_' + new Date().toString().replace(/ /g, '_') + '.' + fileExtension(name);
	} else {
		return Math.random().toString(36).substring(7) + '.' + fileExtension(name);
	}
}
// clean the file object from unwanted attributes
function restructure(file){
	return {
		path: file.path,
		type: file.type,
		size: file.size,
		name: file.name
	};
}
// moving the files
function moveFile(files, options, callback){
	let l = 1;
	// loop into the files array and move
	// the file to the the path that used want
	options.filefields.forEach(function(field) {
		if (typeof files[field] == 'undefined') {
			// check if the field is medtory or not
			if (options.used == "maybe") {
				next()
			} else if (options.used == "must") {
				return callback({code: "ExternalError", messege: "Cannot read property '" + field + "' of undefined"}, null)
			}
		}else {
		    var newPath = options.uploadDir + filename(files[field].name, options);
			fs.move(files[field].path,  newPath , function (err) {
				if (err) return callback({code: "InternalError", messege: "Error happen while uploading."}, null)
				files[field].path = newPath;
				files[field] = restructure(files[field]);
				if (options.filefields.length == l) {
				 	return callback(null, files);
				}else{
					l++;
				}
			});
		}
	});

}
module.exports = Upload;<|endoftext|>


<commit_before>function copy(text) {
  var range = document.createRange();
  var newDiv = document.createElement("div");
  var newContent = document.createTextNode(text);
  newDiv.appendChild(newContent);
  document.body.appendChild(newDiv);
  range.selectNode(newDiv);
  window.getSelection().addRange(range);
  try {
    var successful = document.execCommand('copy');
    if (!successful) {
      throw(new Error('copy command was unsuccessful'));
    }
  } catch(err) {
    console.error('unable to copy, trying IE specific stuff');
    try {
      window.clipboardData.setData("Text", text);
    } catch(err) {
      console.error('unable to copy, fallback to prompt');
      window.prompt("Copy to clipboard: Ctrl+C, Enter", text);
    }
  } finally {
    // Remove the selections - NOTE: Should use
    // removeRange(range) when it is supported
    document.body.removeChild(newDiv);
    window.getSelection().removeAllRanges();
  }
}
module.exports = copy;<commit_msg>throw is not a function<commit_after>function copy(text) {
  var range = document.createRange();
  var newDiv = document.createElement("div");
  var newContent = document.createTextNode(text);
  newDiv.appendChild(newContent);
  document.body.appendChild(newDiv);
  range.selectNode(newDiv);
  window.getSelection().addRange(range);
  try {
    var successful = document.execCommand('copy');
    if (!successful) {
      throw new Error('copy command was unsuccessful');
    }
  } catch(err) {
    console.error('unable to copy, trying IE specific stuff');
    try {
      window.clipboardData.setData("Text", text);
    } catch(err) {
      console.error('unable to copy, fallback to prompt');
      window.prompt("Copy to clipboard: Ctrl+C, Enter", text);
    }
  } finally {
    // Remove the selections - NOTE: Should use
    // removeRange(range) when it is supported
    document.body.removeChild(newDiv);
    window.getSelection().removeAllRanges();
  }
}
module.exports = copy;<|endoftext|>