<commit_before>'use strict';
const join = require('path').join;

const lazyReq = require('lazy-req')(require);

const request = lazyReq('request');
const semver = lazyReq('semver');
const del = lazyReq('del');

const manager = lazyReq('bin-manager');

const osFilterObj = lazyReq('os-filter-obj');

const BIN_PATH = 'bin';

const VERSION_LIST_ENDPOINT = 'https://arduino-cli.github.io/arduino-version/list';

const MIRRORS = [{
  os: 'win32',
  uri: 'https://github.com/arduino-cli/arduino-version/releases/download/{{version}}/arduino-{{version}}-windows.zip',
  bin: 'arduino_debug.exe'
}, {
  os: 'darwin',
  uri: 'https://github.com/arduino-cli/arduino-version/releases/download/{{version}}/arduino-{{version}}-macosx.zip',
  bins: [{
    bin: 'Contents/MacOS/Arduino',
    version: '>=1.6.0'
  }, {
    bin: 'Contents/MacOS/JavaAppLauncher',
    version: '<=1.5.8'
  }]
}, {
  os: 'linux',
  arch: 'x32',
  uri: 'https://github.com/arduino-cli/arduino-version/releases/download/{{version}}/arduino-{{version}}-linux32.zip',
  bin: 'arduino'
}, {
  os: 'linux',
  arch: 'x64',
  uri: 'https://github.com/arduino-cli/arduino-version/releases/download/{{version}}/arduino-{{version}}-linux64.zip',
  bin: 'arduino'
}, {
  os: 'linux',
  arch: 'arm',
  uri: 'https://github.com/arduino-cli/arduino-version/releases/download/{{version}}/arduino-{{version}}-linuxarm.zip',
  bin: 'arduino'
}];

const SPLASH_SCREEN = [{
  os: 'win32',
  img: 'lib/splash.png'
}, {
  os: 'darwin',
  img: 'Contents/Java/lib/splash.png'
}];

/**
 * Initialize a new `Arduino`
 *
 * @param {object} [opts]
 * @api public
 */

function arduino(opts) {
  const version = opts.version || 'latest';
  const binPath = opts.path || BIN_PATH;
  const binSlug = opts.tag ? ('-' + opts.tag) : '';
  let inited = false;
  let bin;

  /**
   * Runs the arduino binary
   *
   * @param {array}    [argv]
   * @param {function} callback
   * @api public
   */
  function run(argv, callback) {
    if (callback === undefined) {
      callback = argv;
      argv = [];
    }
    if (inited) {
      bin.run(argv, callback);
      return;
    }

    getFullVersion((err, version) => {
      if (err) {
        callback(err);
        return;
      }
      init(version);
      bin.run(argv, callback);
    });
  }

  /**
   * Load/download the current version of the binary
   *
   * @param {function} callback
   * @api public
   */
  function load(callback) {
    const opts = {
      extract: true,
      strip: 1
    };

    if (inited) {
      bin.load(opts, err => callback(err));
      return;
    }

    getFullVersion((err, version) => {
      if (err) {
        callback(err);
        return;
      }
      init(version);
      bin.load(opts, err => {
        if (err) {
          callback(err);
          return;
        }

        // HACK: remove the splash.png file because the arduino IDE does not
        // hide it in command line mode.
        // See https://github.com/arduino/Arduino/blob/master/build/shared/manpage.adoc
        const splash = osFilterObj()(SPLASH_SCREEN);
        if (splash.length === 0) {
          callback();
          return;
        }
        const dirs = splash.map(s => {
          return join(bin.path(), s.img);
        });
        del()(dirs)
          .then(() => callback())
          .catch(err => callback(err));
      });
    });
  }

  /**
   * Removes the current version of the binary
   *
   * @param  {function} callback
   * @api public
   */
  function unload(callback) {
    if (inited) {
      bin.unload(err => callback(err));
      return;
    }

    getFullVersion((err, version) => {
      if (err) {
        callback(err);
        return;
      }
      init(version);
      bin.unload(err => callback(err));
    });
  }

  /**
   * Checks if a version is available.
   *
   * @param  {function} callback
   * @api private
   */
  function getFullVersion(callback) {
    getVersionsList((err, vers) => {
      if (err) {
        callback(err);
        return;
      }
      if (version === 'latest') {
        callback(null, vers[vers.length - 1]);
        return;
      }
      const valid = vers.some(v => v === version);
      if (!valid) {
        callback(new Error('The version provided is not available'));
        return;
      }
      callback(null, version);
    });
  }

  /**
   * Retrieves the list of arduino ide versions available
   *
   * @param  {function} callback
   * @api private
  */
  function getVersionsList(callback) {
    request()(VERSION_LIST_ENDPOINT, (err, res, body) => {
      if (err) {
        callback('Cannot get the version list: ' + err);
        return;
      }
      if (res.statusCode !== 200) {
        callback('Cannot get the version list: ' + res.statusCode);
        return;
      }
      const vers = body.split(',').map(v => semver().clean(v));
      callback(null, vers);
    });
  }

  /**
   * Initializes the binary mirrors
   *
   * @param {string} version
   * @api private
   */
  function init(version) {
    const slug = 'arduino-' + version + binSlug;
    bin = manager()(binPath, slug);
    MIRRORS.forEach(mirror => {
      bin.src(mirror.uri.replace(/{{version}}/g, version), mirror.os, mirror.arch);

      if (mirror.os === process.platform) {
        if (Array.isArray(mirror.bins)) {
          mirror.bins.some(b => {
            if (semver().satisfies(version, b.version)) {
              bin.use(b.bin.replace(/{{version}}/g, version));
              return true;
            }
            return false;
          });
          return;
        }
        bin.use(mirror.bin.replace(/{{version}}/g, version));
      }
    });

    inited = true;
  }

  /**
   * Gets the path to the binary
   *
   * @api public
   */
  function binary(callback) {
    if (inited) {
      callback(null, bin.bin());
      return;
    }
    getFullVersion((err, version) => {
      if (err) {
        callback(err);
        return;
      }
      init(version);
      callback(null, bin.bin());
    });
  }

  /**
   * Gets the path to the folder containing the Arduino IDE in use
   *
   * @api public
   */
  function path(callback) {
    if (inited) {
      callback(null, bin.path());
      return;
    }
    getFullVersion((err, version) => {
      if (err) {
        callback(err);
        return;
      }
      init(version);
      callback(null, bin.path());
    });
  }

  return {
    run,
    load,
    unload,
    binary,
    path
  };
}

module.exports = arduino;
<commit_msg>Remove duplicated code<commit_after>'use strict';
const join = require('path').join;

const lazyReq = require('lazy-req')(require);

const request = lazyReq('request');
const semver = lazyReq('semver');
const del = lazyReq('del');

const manager = lazyReq('bin-manager');

const osFilterObj = lazyReq('os-filter-obj');

const BIN_PATH = 'bin';

const VERSION_LIST_ENDPOINT = 'https://arduino-cli.github.io/arduino-version/list';

const MIRRORS = [{
  os: 'win32',
  uri: 'https://github.com/arduino-cli/arduino-version/releases/download/{{version}}/arduino-{{version}}-windows.zip',
  bin: 'arduino_debug.exe'
}, {
  os: 'darwin',
  uri: 'https://github.com/arduino-cli/arduino-version/releases/download/{{version}}/arduino-{{version}}-macosx.zip',
  bins: [{
    bin: 'Contents/MacOS/Arduino',
    version: '>=1.6.0'
  }, {
    bin: 'Contents/MacOS/JavaAppLauncher',
    version: '<=1.5.8'
  }]
}, {
  os: 'linux',
  arch: 'x32',
  uri: 'https://github.com/arduino-cli/arduino-version/releases/download/{{version}}/arduino-{{version}}-linux32.zip',
  bin: 'arduino'
}, {
  os: 'linux',
  arch: 'x64',
  uri: 'https://github.com/arduino-cli/arduino-version/releases/download/{{version}}/arduino-{{version}}-linux64.zip',
  bin: 'arduino'
}, {
  os: 'linux',
  arch: 'arm',
  uri: 'https://github.com/arduino-cli/arduino-version/releases/download/{{version}}/arduino-{{version}}-linuxarm.zip',
  bin: 'arduino'
}];

const SPLASH_SCREEN = [{
  os: 'win32',
  img: 'lib/splash.png'
}, {
  os: 'darwin',
  img: 'Contents/Java/lib/splash.png'
}];

/**
 * Initialize a new `Arduino`
 *
 * @param {object} [opts]
 * @api public
 */

function arduino(opts) {
  const version = opts.version || 'latest';
  const binPath = opts.path || BIN_PATH;
  const binSlug = opts.tag ? ('-' + opts.tag) : '';
  let inited = false;
  let bin;

  /**
   * Runs the arduino binary
   *
   * @param {array}    [argv]
   * @param {function} callback
   * @api public
   */
  function run(argv, callback) {
    if (callback === undefined) {
      callback = argv;
      argv = [];
    }
    if (inited) {
      bin.run(argv, callback);
      return;
    }

    init(err => {
      if (err) {
        callback(err);
        return;
      }
      bin.run(argv, callback);
    });
  }

  /**
   * Load/download the current version of the binary
   *
   * @param {function} callback
   * @api public
   */
  function load(callback) {
    const opts = {
      extract: true,
      strip: 1
    };

    if (inited) {
      bin.load(opts, err => callback(err));
      return;
    }

    init(err => {
      if (err) {
        callback(err);
        return;
      }
      bin.load(opts, err => {
        if (err) {
          callback(err);
          return;
        }

        // HACK: remove the splash.png file because the arduino IDE does not
        // hide it in command line mode.
        // See https://github.com/arduino/Arduino/blob/master/build/shared/manpage.adoc
        const splash = osFilterObj()(SPLASH_SCREEN);
        if (splash.length === 0) {
          callback();
          return;
        }
        const dirs = splash.map(s => {
          return join(bin.path(), s.img);
        });
        del()(dirs)
          .then(() => callback())
          .catch(err => callback(err));
      });
    });
  }

  /**
   * Removes the current version of the binary
   *
   * @param  {function} callback
   * @api public
   */
  function unload(callback) {
    if (inited) {
      bin.unload(callback);
      return;
    }

    init(err => {
      if (err) {
        callback(err);
        return;
      }
      bin.unload(callback);
    });
  }

  /**
   * Checks if a version is available.
   *
   * @param  {function} callback
   * @api private
   */
  function getFullVersion(callback) {
    getVersionsList((err, vers) => {
      if (err) {
        callback(err);
        return;
      }
      if (version === 'latest') {
        callback(null, vers[vers.length - 1]);
        return;
      }
      const valid = vers.some(v => v === version);
      if (!valid) {
        callback(new Error('The version provided is not available'));
        return;
      }
      callback(null, version);
    });
  }

  /**
   * Retrieves the list of arduino ide versions available
   *
   * @param  {function} callback
   * @api private
  */
  function getVersionsList(callback) {
    request()(VERSION_LIST_ENDPOINT, (err, res, body) => {
      if (err) {
        callback('Cannot get the version list: ' + err);
        return;
      }
      if (res.statusCode !== 200) {
        callback('Cannot get the version list: ' + res.statusCode);
        return;
      }
      const vers = body.split(',').map(v => semver().clean(v));
      callback(null, vers);
    });
  }

  /**
   * Initializes the binary mirrors
   *
   * @param {string} version
   * @api private
   */
  function init(callback) {
    getFullVersion((err, version) => {
      if (err) {
        callback(err);
        return;
      }
      const slug = 'arduino-' + version + binSlug;
      bin = manager()(binPath, slug);
      MIRRORS.forEach(mirror => {
        bin.src(mirror.uri.replace(/{{version}}/g, version), mirror.os, mirror.arch);

        if (mirror.os === process.platform) {
          if (Array.isArray(mirror.bins)) {
            mirror.bins.some(b => {
              if (semver().satisfies(version, b.version)) {
                bin.use(b.bin.replace(/{{version}}/g, version));
                return true;
              }
              return false;
            });
            return;
          }
          bin.use(mirror.bin.replace(/{{version}}/g, version));
        }
      });

      inited = true;
      callback();
    });
  }

  /**
   * Gets the path to the binary
   *
   * @api public
   */
  function binary(callback) {
    if (inited) {
      callback(null, bin.bin());
      return;
    }
    init(err => {
      if (err) {
        callback(err);
        return;
      }
      callback(null, bin.bin());
    });
  }

  /**
   * Gets the path to the folder containing the Arduino IDE in use
   *
   * @api public
   */
  function path(callback) {
    if (inited) {
      callback(null, bin.path());
      return;
    }
    init(err => {
      if (err) {
        callback(err);
      }
      callback(null, bin.path());
    });
  }

  return {
    run,
    load,
    unload,
    binary,
    path
  };
}

module.exports = arduino;
<|endoftext|>


<commit_before>var request = require('request');
var PythonShell = require('python-shell');
var Relayr = require('relayr');

//relayr information
var app_id = process.env.APP_ID;
var token = process.env.TOKEN;
var dev_id = process.env.DEVICE_ID;
var topic = process.env.TOPIC;

// Global variable for current state (ex: On/Off)
var state = "0";

var relayr = new Relayr(app_id);
relayr.connect(token, dev_id);

relayr.on('connect', function() {
  callAdafruit();
});

relayr.on('data', function(topic, msg) {
  if (msg.readings[0].meaning == "temperature") {
    if (msg.readings[0].value >= 30.3 && state != "off") {
      pythonCommand('off', 'off');
    }
  }
});

//adafruit information
var aioKey = process.env.AIO_KEY;
var options = {
  url: 'https://io.adafruit.com/api/feeds/586533',
  headers: {
    'X-AIO-KEY' : aioKey
  }
};

function callAdafruit() {
  request(options, function (err, res, body) {
    if (err) console.log(err);

    if (res.statusCode != 200) {
      setTimeout(function() {
        callAdafruit();
      }, 15000);
    } else {
      var data = JSON.parse(body);

      if (state === "off") {
        // Emergency shut off, wait 20 seconds before allowing user input again.
        setTimeout(function() {
          state = "0";
          callAdafruit();
        }, 20000);
      } else if (state === data.last_value) {
        setTimeout(function() {
          callAdafruit();
        }, 5000);
      } else {
        switch(data.last_value) {
          case "0":
            pythonCommand('off', data.last_value);
            break;
          case "50":
            pythonCommand('low', data.last_value);
            break;
          case "100":
            pythonCommand('on', data.last_value);
            break;
          default:
            pythonCommand('off', data.last_value);
        }
      }

      setTimeout(function() {
        callAdafruit();
      }, 5000);
    }
  });
}

function pythonCommand(command, value) {
  var pyOptions = {
    "args": command
  };

  PythonShell.run('servo2.py', pyOptions, function(err, results) {
    if (err) console.log(err);
    state = value;
    console.log("State changed to: " + state);
    console.log(results);
  });
}
<commit_msg>config update<commit_after>var request = require('request');
var PythonShell = require('python-shell');
var Relayr = require('relayr');
var config = require('./config');

//relayr information
var app_id = config.APP_ID;
var token = config.TOKEN;
var dev_id = config.DEVICE_ID;
var topic = config.TOPIC;

// Global variable for current state (ex: On/Off)
var state = "0";

var relayr = new Relayr(app_id);
relayr.connect(token, dev_id);

relayr.on('connect', function() {
  callAdafruit();
});

relayr.on('data', function(topic, msg) {
  if (msg.readings[0].meaning == "temperature") {
    if (msg.readings[0].value >= 30.3 && state != "off") {
      pythonCommand('off', 'off');
    }
  }
});

//adafruit information
var aioKey = config.AIO_KEY;
var options = {
  url: 'https://io.adafruit.com/api/feeds/586533',
  headers: {
    'X-AIO-KEY' : aioKey
  }
};

function callAdafruit() {
  request(options, function (err, res, body) {
    if (err) console.log(err);

    if (res.statusCode != 200) {
      setTimeout(function() {
        callAdafruit();
      }, 15000);
    } else {
      var data = JSON.parse(body);

      if (state === "off") {
        // Emergency shut off, wait 20 seconds before allowing user input again.
        setTimeout(function() {
          state = "0";
          callAdafruit();
        }, 20000);
      } else if (state === data.last_value) {
        setTimeout(function() {
          callAdafruit();
        }, 5000);
      } else {
        switch(data.last_value) {
          case "0":
            pythonCommand('off', data.last_value);
            break;
          case "50":
            pythonCommand('low', data.last_value);
            break;
          case "100":
            pythonCommand('on', data.last_value);
            break;
          default:
            pythonCommand('off', data.last_value);
        }
      }

      setTimeout(function() {
        callAdafruit();
      }, 5000);
    }
  });
}

function pythonCommand(command, value) {
  var pyOptions = {
    "args": command
  };

  PythonShell.run('servo2.py', pyOptions, function(err, results) {
    if (err) console.log(err);
    state = value;
    console.log("State changed to: " + state);
    console.log(results);
  });
}
<|endoftext|>


<commit_before>var ogs = require('open-graph-scraper');
var express = require('express');
var ElasticSearch = require('elasticsearch');
var moment = require("moment-timezone");
var camoUrl = require('camo-url')({
	host : process.env.CAMO_HOST,
	key : process.env.CAMO_KEY
});
var URL = require('url');
var app = express();
var es = new ElasticSearch.Client({
	host : process.env.ES_URL,
	apiVersion : process.env.ES_VERSION
});

var esIndex = process.env.ES_INDEX;
var esType = process.env.ES_TYPE;

var INFO = "INFO";
var WARN = "WARN";
var ERR = "ERROR";

var BLOCKED_EXTENSIONS = ['pdf', 'gif', 'jpg', 'jpeg', 'png', 'svg'];

var CACHABLE_NETWORK_ERRORS = [
	'HPE_INVALID_CONSTANT' // May trigger if eg chipsoft Sharepoint send a content-length of 0 but secretly appends data
];
var NETWORK_ERRORS = [].concat(CACHABLE_NETWORK_ERRORS);
var CACHABLE_PAGE_ERRORS = ['Page Not Found'];
var CACHABLE_ERRORS = CACHABLE_NETWORK_ERRORS.concat(CACHABLE_PAGE_ERRORS);

var cacheInDays = parseInt(process.env.CACHE_IN_DAYS, 10) || 28;

function stringToUnderscore(input) {
	return input.replace(/([A-Z])/g, function ($1) {
		return "_" + $1.toLowerCase();
	});
}

function log(level, message) {
	console.log(JSON.stringify({time : moment(new Date()).format(), severity : level, message : message}));
}

var timeout = Number(process.env.HTTP_TIMEOUT) || 10000;
log(INFO, "Using a HTTP timeout of " + timeout + " milliseconds");

function cacheExpired(date) {
	return moment(date).add(cacheInDays, 'days').isBefore(moment(new Date()));
}

function defaultOutput(url) {
	return Object.assign({}, {
		_url : url,
		_scrapedAt : parseInt(moment(new Date()).format('x'), 10),
		_cacheResponse : false,
		data : {}
	});
}

function postProcess(url, data) {
	var result = defaultOutput(url);

	// Format images for their special cases
	['ogImage', 'twitterImage'].forEach(function (key) {
		if (data[key]) {
			if (!data[key].url) {
				delete data[key];
			} else if (data[key].url) {
				var imageUrl = resolveRelative(data[key].url, url);
				if (process.env.CAMO_KEY && process.env.CAMO_HOST) {
					imageUrl = camoUrl(imageUrl);
				}
				var image = Object.assign({}, {value : imageUrl});
				Object.keys(data[key]).filter(function (e) {
					return e !== 'url';
				}).forEach(function (innerKey) {
					image[innerKey] = [{value : data[key][innerKey]}];
				});
				data[key] = image;
			}
		}
	});

	// Now map this to useful structures
	Object.keys(data).forEach(function (originalKey) {
		if (data[originalKey] === null || typeof data[originalKey] === 'undefined') {
			return;
		}

		var key;
		if (originalKey.startsWith('og')) {
			key = stringToUnderscore(originalKey.substring(2)).substring(1);
		}
		else if (originalKey.startsWith('twitter')) {
			key = stringToUnderscore(originalKey);
		} else {
			return;
		}

		if (data[originalKey].value) {
			result.data[key] = [data[originalKey]]
		} else {
			result.data[key] = [{value : data[originalKey]}]
		}
	});

	if (!result.data.url) {
		result.data.url = [{value : url}];
	}

	return result;
}

function resolveRelative(path, base) {
	// Somebody forgot to properly set a protocol on an otherwise absolute url
	if (path.indexOf("www.") === 0) {
		return 'http://' + path;
	}

	// Absolute URL
	if (path.match(/^[a-z]*:\/\//)) {
		return path;
	}
	// Protocol relative URL
	if (path.indexOf("//") === 0) {
		return base.replace(/\/\/.*/, path)
	}
	// Upper directory
	if (path.indexOf("../") === 0) {
		return resolveRelative(path.slice(3), base.replace(/\/[^\/]*$/, ''));
	}
	// Relative to the root
	if (path.indexOf('/') === 0) {
		if (!base.endsWith('/')) {
			base += '/';
		}
		var match = base.match(/(\w*:\/\/)?[^\/]*\//) || [base];
		return match[0] + path.slice(1);
	}
	//relative to the current directory
	return base.replace(/\/[^\/]*$/, "") + '/' + path.replace(/^\.\//, '');
}

if (process.env.ES_URL) {
	log(INFO, "Connected to Elasticsearch " + process.env.ES_VERSION + " on " + process.env.ES_URL + " using index '" + esIndex + "' with type '" + esType + "'. Using a cache of " + cacheInDays + " days.");
}

function cacheEntryIsValid(error, response, url) {
	if (!process.env.ES_URL) {
		// No ES cache entry is set
		log(WARN, "No ES cache specified, so skipping it");
		return false;
	}
	if (error) {
		if (error.status === 404) {
			log(INFO, "Getting new value for '" + url + "' since never seen that before");
		} else {
			log(WARN, "Got an error [" + error.status + "] while fetching URL '" + url + "' from ES cache");
		}
		return false;
	}
	if (!response._source || !response._source._scrapedAt) {
		log(WARN, 'Formatting error in cache, refetching ' + url);
		return false;
	}
	if (cacheExpired(response._source._scrapedAt)) {
		// cache is just expired
		log(INFO, "Getting new value for '" + url + "' since the cache expired");
		return false;
	}
	return true;
}

function workWorkWork(req, res) {
	var urlToFetch = req.query.url;
	var pathname = URL.parse(urlToFetch).pathname;
	var blockedResource = BLOCKED_EXTENSIONS.filter(function (extension) {
			return pathname.endsWith(extension);
		}).length > 0;
	if (blockedResource) {
		// Return standard format for misbehaving clients
		var blockedError = defaultOutput(urlToFetch);
		blockedError.err = 'This resource is blocked from fetching opengraph data';
		res.status(403).json(blockedError);
		return;
	}

	var encodedUrl = encodeURI(urlToFetch);
	es.get({index : esIndex, type : esType, id : encodedUrl}, function (err, response) {
		if (cacheEntryIsValid(err, response, urlToFetch)) {
			// If there is an error, we do not have the opengraph data so we return a 404
			var statusCode = !response._source.err ? 200 : 404;
			res.status(statusCode).json(response._source);
		} else {
			var options = {
				url : encodeURI(urlToFetch),
				timeout : timeout
			};
			ogs(options, function (err, ogData) {
				var resultData;
				if (err || !ogData.success) {
					// Return the data to the client
					resultData = defaultOutput(urlToFetch);
					resultData.err = ogData.err;

					var status = ogData.errorDetails && NETWORK_ERRORS.indexOf(ogData.errorDetails.code) !== -1 ?
						406 : // If the network request is borked
						404; // If the page was not found

					res.status(status).json(resultData);

					if (CACHABLE_ERRORS.indexOf(ogData.err) === -1) {
						// If this is a more permanent failure, we cache it
						log(ERR, 'Error while fetching OG data for ' + urlToFetch + ': ' + JSON.stringify(ogData));
						return;
					}
				} else {
					// All went well, postprocess and send to client
					resultData = postProcess(urlToFetch, ogData.data);
					res.json(resultData);
				}

				if (process.env.ES_URL) {
					resultData._cacheResponse = true;
					es.index({index : esIndex, type : esType, id : encodedUrl, body : resultData}, function (err) {
						if (err) {
							log(WARN, 'Could not save value to cache due to error: ' + err);
						}
					});
				}
			});
		}
	});
}

function ditchDitchDitch(req, res) {
	var urlToDelete = req.query.url;

	var encodedUrl = encodeURI(urlToDelete);
	if (!process.env.ES_URL) {
		// Technically not, but the effect is the same for the caller
		res.status(200).json({message: 'The url has been deleted from the cache', url: urlToDelete});
		return;
	}
	es.delete({index : esIndex, type : esType, id : encodedUrl}, function (err, response) {
		if (err && err.statusCode !== 404) {
			// If there is an error, we do not have the opengraph data so we return a 404
			log(ERR, 'An error occured while deleting data from the cache: ' + err);
			res.status(500).json({message: 'The url could not be deleted due to an internal error', url: urlToDelete});
		} else {
			res.status(200).json({message: 'The url has been deleted from the cache', url: urlToDelete});
		}
	});
}

app.get('/opengraph', workWorkWork);
app.delete('/opengraph', ditchDitchDitch);
app.get('/_health', function (req, res) {
	res.end('Jolly good here');
});
app.listen(7070, function () {
	log(INFO, 'Server is listening');
});

<commit_msg>Resolve the image relative to the canonical url (for redirects as bit.ly)<commit_after>var ogs = require('open-graph-scraper');
var express = require('express');
var ElasticSearch = require('elasticsearch');
var moment = require("moment-timezone");
var camoUrl = require('camo-url')({
	host : process.env.CAMO_HOST,
	key : process.env.CAMO_KEY
});
var URL = require('url');
var app = express();
var es = new ElasticSearch.Client({
	host : process.env.ES_URL,
	apiVersion : process.env.ES_VERSION
});

var esIndex = process.env.ES_INDEX;
var esType = process.env.ES_TYPE;

var INFO = "INFO";
var WARN = "WARN";
var ERR = "ERROR";

var BLOCKED_EXTENSIONS = ['pdf', 'gif', 'jpg', 'jpeg', 'png', 'svg'];

var CACHABLE_NETWORK_ERRORS = [
	'HPE_INVALID_CONSTANT' // May trigger if eg chipsoft Sharepoint send a content-length of 0 but secretly appends data
];
var NETWORK_ERRORS = [].concat(CACHABLE_NETWORK_ERRORS);
var CACHABLE_PAGE_ERRORS = ['Page Not Found'];
var CACHABLE_ERRORS = CACHABLE_NETWORK_ERRORS.concat(CACHABLE_PAGE_ERRORS);

var cacheInDays = parseInt(process.env.CACHE_IN_DAYS, 10) || 28;

function stringToUnderscore(input) {
	return input.replace(/([A-Z])/g, function ($1) {
		return "_" + $1.toLowerCase();
	});
}

function log(level, message) {
	console.log(JSON.stringify({time : moment(new Date()).format(), severity : level, message : message}));
}

var timeout = Number(process.env.HTTP_TIMEOUT) || 10000;
log(INFO, "Using a HTTP timeout of " + timeout + " milliseconds");

function cacheExpired(date) {
	return moment(date).add(cacheInDays, 'days').isBefore(moment(new Date()));
}

function defaultOutput(url) {
	return Object.assign({}, {
		_url : url,
		_scrapedAt : parseInt(moment(new Date()).format('x'), 10),
		_cacheResponse : false,
		data : {}
	});
}

function postProcess(url, data) {
	var result = defaultOutput(url);

	var canonicalUrl = data.ogUrl || url;

	// Format images for their special cases
	['ogImage', 'twitterImage'].forEach(function (key) {
		if (data[key]) {
			if (!data[key].url) {
				delete data[key];
			} else if (data[key].url) {
				var imageUrl = resolveRelative(data[key].url, canonicalUrl);
				if (process.env.CAMO_KEY && process.env.CAMO_HOST) {
					imageUrl = camoUrl(imageUrl);
				}
				var image = Object.assign({}, {value : imageUrl});
				Object.keys(data[key]).filter(function (e) {
					return e !== 'url';
				}).forEach(function (innerKey) {
					image[innerKey] = [{value : data[key][innerKey]}];
				});
				data[key] = image;
			}
		}
	});

	// Now map this to useful structures
	Object.keys(data).forEach(function (originalKey) {
		if (data[originalKey] === null || typeof data[originalKey] === 'undefined') {
			return;
		}

		var key;
		if (originalKey.startsWith('og')) {
			key = stringToUnderscore(originalKey.substring(2)).substring(1);
		}
		else if (originalKey.startsWith('twitter')) {
			key = stringToUnderscore(originalKey);
		} else {
			return;
		}

		if (data[originalKey].value) {
			result.data[key] = [data[originalKey]]
		} else {
			result.data[key] = [{value : data[originalKey]}]
		}
	});

	if (!result.data.url) {
		result.data.url = [{value : url}];
	}

	return result;
}

function resolveRelative(path, base) {
	// Somebody forgot to properly set a protocol on an otherwise absolute url
	if (path.indexOf("www.") === 0) {
		return 'http://' + path;
	}

	// Absolute URL
	if (path.match(/^[a-z]*:\/\//)) {
		return path;
	}
	// Protocol relative URL
	if (path.indexOf("//") === 0) {
		return base.replace(/\/\/.*/, path)
	}
	// Upper directory
	if (path.indexOf("../") === 0) {
		return resolveRelative(path.slice(3), base.replace(/\/[^\/]*$/, ''));
	}
	// Relative to the root
	if (path.indexOf('/') === 0) {
		if (!base.endsWith('/')) {
			base += '/';
		}
		var match = base.match(/(\w*:\/\/)?[^\/]*\//) || [base];
		return match[0] + path.slice(1);
	}
	//relative to the current directory
	return base.replace(/\/[^\/]*$/, "") + '/' + path.replace(/^\.\//, '');
}

if (process.env.ES_URL) {
	log(INFO, "Connected to Elasticsearch " + process.env.ES_VERSION + " on " + process.env.ES_URL + " using index '" + esIndex + "' with type '" + esType + "'. Using a cache of " + cacheInDays + " days.");
}

function cacheEntryIsValid(error, response, url) {
	if (!process.env.ES_URL) {
		// No ES cache entry is set
		log(WARN, "No ES cache specified, so skipping it");
		return false;
	}
	if (error) {
		if (error.status === 404) {
			log(INFO, "Getting new value for '" + url + "' since never seen that before");
		} else {
			log(WARN, "Got an error [" + error.status + "] while fetching URL '" + url + "' from ES cache");
		}
		return false;
	}
	if (!response._source || !response._source._scrapedAt) {
		log(WARN, 'Formatting error in cache, refetching ' + url);
		return false;
	}
	if (cacheExpired(response._source._scrapedAt)) {
		// cache is just expired
		log(INFO, "Getting new value for '" + url + "' since the cache expired");
		return false;
	}
	return true;
}

function workWorkWork(req, res) {
	var urlToFetch = req.query.url;
	var pathname = URL.parse(urlToFetch).pathname;
	var blockedResource = BLOCKED_EXTENSIONS.filter(function (extension) {
			return pathname.endsWith(extension);
		}).length > 0;
	if (blockedResource) {
		// Return standard format for misbehaving clients
		var blockedError = defaultOutput(urlToFetch);
		blockedError.err = 'This resource is blocked from fetching opengraph data';
		res.status(403).json(blockedError);
		return;
	}

	var encodedUrl = encodeURI(urlToFetch);
	es.get({index : esIndex, type : esType, id : encodedUrl}, function (err, response) {
		if (cacheEntryIsValid(err, response, urlToFetch)) {
			// If there is an error, we do not have the opengraph data so we return a 404
			var statusCode = !response._source.err ? 200 : 404;
			res.status(statusCode).json(response._source);
		} else {
			var options = {
				url : encodeURI(urlToFetch),
				timeout : timeout
			};
			ogs(options, function (err, ogData) {
				var resultData;
				if (err || !ogData.success) {
					// Return the data to the client
					resultData = defaultOutput(urlToFetch);
					resultData.err = ogData.err;

					var status = ogData.errorDetails && NETWORK_ERRORS.indexOf(ogData.errorDetails.code) !== -1 ?
						406 : // If the network request is borked
						404; // If the page was not found

					res.status(status).json(resultData);

					if (CACHABLE_ERRORS.indexOf(ogData.err) === -1) {
						// If this is a more permanent failure, we cache it
						log(ERR, 'Error while fetching OG data for ' + urlToFetch + ': ' + JSON.stringify(ogData));
						return;
					}
				} else {
					// All went well, postprocess and send to client
					resultData = postProcess(urlToFetch, ogData.data);
					res.json(resultData);
				}

				if (process.env.ES_URL) {
					resultData._cacheResponse = true;
					es.index({index : esIndex, type : esType, id : encodedUrl, body : resultData}, function (err) {
						if (err) {
							log(WARN, 'Could not save value to cache due to error: ' + err);
						}
					});
				}
			});
		}
	});
}

function ditchDitchDitch(req, res) {
	var urlToDelete = req.query.url;

	var encodedUrl = encodeURI(urlToDelete);
	if (!process.env.ES_URL) {
		// Technically not, but the effect is the same for the caller
		res.status(200).json({message: 'The url has been deleted from the cache', url: urlToDelete});
		return;
	}
	es.delete({index : esIndex, type : esType, id : encodedUrl}, function (err, response) {
		if (err && err.statusCode !== 404) {
			// If there is an error, we do not have the opengraph data so we return a 404
			log(ERR, 'An error occured while deleting data from the cache: ' + err);
			res.status(500).json({message: 'The url could not be deleted due to an internal error', url: urlToDelete});
		} else {
			res.status(200).json({message: 'The url has been deleted from the cache', url: urlToDelete});
		}
	});
}

app.get('/opengraph', workWorkWork);
app.delete('/opengraph', ditchDitchDitch);
app.get('/_health', function (req, res) {
	res.end('Jolly good here');
});
app.listen(7070, function () {
	log(INFO, 'Server is listening');
});

<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var json2xml = require('json2xml');
var xml2json = require('xml2json');
var client = require('request');

module.exports = MyVirtualMerchant;

function MyVirtualMerchant(options) {
    this.merchant_id = options.merchant_id;
    this.user_id = options.user_id;
    this.ssl_pin = options.ssl_pin;
    this.test_mode = options.test_mode || false;
    this.endpoints = this.resolveEndpoints();
}

MyVirtualMerchant.prototype.resolveEndpoints = function () {
    return (this.test_mode) ? {
        process: 'https://demo.myvirtualmerchant.com/VirtualMerchantDemo/process.do',
        process_batch: 'https://demo.myvirtualmerchant.com/VirtualMerchantDemo/processBatch.do',
        process_xml: 'https://demo.myvirtualmerchant.com/VirtualMerchantDemo/processxml.do',
        account_xml: 'https://demo.myvirtualmerchant.com/VirtualMerchantDemo/accountxml.do'
    } : {
        process: 'https://demo.myvirtualmerchant.com/VirtualMerchant/process.do',
        process_batch: 'https://demo.myvirtualmerchant.com/VirtualMerchant/processBatch.do',
        process_xml: 'https://demo.myvirtualmerchant.com/VirtualMerchant/processxml.do',
        account_xml: 'https://demo.myvirtualmerchant.com/VirtualMerchant/accountxml.do'
    };
};

MyVirtualMerchant.prototype.signRequest = function (request) {
    return _.extend(request, {
        ssl_merchant_id: this.merchant_id,
        ssl_user_id: this.user_id,
        ssl_pin: this.ssl_pin
    });
};

MyVirtualMerchant.prototype.processRequest = function (request, callback) {
    client.post(this.endpoints.process_xml, {
        form: {
            xmldata: json2xml({
                txn: this.signRequest(request)
            })
        }
    }, function (error, response, body) {
        try {
            var result = JSON.parse(xml2json.toJson(body)).txn;
            if (result.errorMessage)
                return callback && callback(result);
            return callback && callback(error, result);
        } catch (e) {
            return callback && callback(result);
        }
    });
};

//mandatory
//ssl_transaction_type, ssl_cardNumber, ssl_exp_date, ssl_amount (+ merchants credentials)
MyVirtualMerchant.prototype.doPurchase = function (order, prospect, creditcard, callback) {

    var query = {
        ssl_transaction_type: 'ccsale',
        ssl_card_number: creditcard.number.trim().replace(/ /g, ''), //a lot of assumptions
        ssl_exp_date: creditcard.expiration.split(' / ').join(''),
        ssl_amount: order.converted_amount,
        ssl_cvv2cvc2_indicator: 1, //this should not be mandatory
        ssl_verify: 'Y'//this should not be mandatory
    };
    if (prospect) {
        query.ssl_customer_code = prospect.id;
        query.ssl_first_name = prospect.firstname;
        query.ssl_last_name = prospect.lastname;
        query.ssl_email = prospect.email;
        query.ssl_phone = prospect.phone;

        if (prospect.billing) {
            query.ssl_avs_address = prospect.billing.adress;
            query.ssl_avs_zip = prospect.billing.zipcode;
            query.ssl_city = prospect.billing.city;
            query.ssl_zip = prospect.billing.zipcode;
            query.ssl_state = prospect.billing.region;
            query.ssl_country = prospect.billing.country;
        }
    }

    this.processRequest(query, callback);
}
;

MyVirtualMerchant.prototype.doRefund = function (data, callback) {
    this.processRequest({
        ssl_transaction_type: 'ccreturn',
        ssl_txn_id: data.txn_id,
        ssl_amount: data.amount
    }, callback);
};

MyVirtualMerchant.prototype.doVoid = function (data, callback) {
    this.processRequest({
        ssl_transaction_type: 'ccvoid',
        ssl_txn_id: data.txn_id
    }, callback);
};
<commit_msg>Typo on address field.<commit_after>'use strict';

var _ = require('lodash');
var json2xml = require('json2xml');
var xml2json = require('xml2json');
var client = require('request');

module.exports = MyVirtualMerchant;

function MyVirtualMerchant(options) {
    this.merchant_id = options.merchant_id;
    this.user_id = options.user_id;
    this.ssl_pin = options.ssl_pin;
    this.test_mode = options.test_mode || false;
    this.endpoints = this.resolveEndpoints();
}

MyVirtualMerchant.prototype.resolveEndpoints = function () {
    return (this.test_mode) ? {
        process: 'https://demo.myvirtualmerchant.com/VirtualMerchantDemo/process.do',
        process_batch: 'https://demo.myvirtualmerchant.com/VirtualMerchantDemo/processBatch.do',
        process_xml: 'https://demo.myvirtualmerchant.com/VirtualMerchantDemo/processxml.do',
        account_xml: 'https://demo.myvirtualmerchant.com/VirtualMerchantDemo/accountxml.do'
    } : {
        process: 'https://demo.myvirtualmerchant.com/VirtualMerchant/process.do',
        process_batch: 'https://demo.myvirtualmerchant.com/VirtualMerchant/processBatch.do',
        process_xml: 'https://demo.myvirtualmerchant.com/VirtualMerchant/processxml.do',
        account_xml: 'https://demo.myvirtualmerchant.com/VirtualMerchant/accountxml.do'
    };
};

MyVirtualMerchant.prototype.signRequest = function (request) {
    return _.extend(request, {
        ssl_merchant_id: this.merchant_id,
        ssl_user_id: this.user_id,
        ssl_pin: this.ssl_pin
    });
};

MyVirtualMerchant.prototype.processRequest = function (request, callback) {
    client.post(this.endpoints.process_xml, {
        form: {
            xmldata: json2xml({
                txn: this.signRequest(request)
            })
        }
    }, function (error, response, body) {
        try {
            var result = JSON.parse(xml2json.toJson(body)).txn;
            if (result.errorMessage)
                return callback && callback(result);
            return callback && callback(error, result);
        } catch (e) {
            return callback && callback(result);
        }
    });
};

//mandatory
//ssl_transaction_type, ssl_cardNumber, ssl_exp_date, ssl_amount (+ merchants credentials)
MyVirtualMerchant.prototype.doPurchase = function (order, prospect, creditcard, callback) {

    var query = {
        ssl_transaction_type: 'ccsale',
        ssl_card_number: creditcard.number.trim().replace(/ /g, ''), //a lot of assumptions
        ssl_exp_date: creditcard.expiration.split(' / ').join(''),
        ssl_amount: order.converted_amount,
        ssl_cvv2cvc2_indicator: 1, //this should not be mandatory
        ssl_verify: 'Y'//this should not be mandatory
    };
    if (prospect) {
        query.ssl_customer_code = prospect.id;
        query.ssl_first_name = prospect.firstname;
        query.ssl_last_name = prospect.lastname;
        query.ssl_email = prospect.email;
        query.ssl_phone = prospect.phone;

        if (prospect.billing) {
            query.ssl_avs_address = prospect.billing.address;
            query.ssl_avs_zip = prospect.billing.zipcode;
            query.ssl_city = prospect.billing.city;
            query.ssl_zip = prospect.billing.zipcode;
            query.ssl_state = prospect.billing.region;
            query.ssl_country = prospect.billing.country;
        }
    }

    this.processRequest(query, callback);
}
;

MyVirtualMerchant.prototype.doRefund = function (data, callback) {
    this.processRequest({
        ssl_transaction_type: 'ccreturn',
        ssl_txn_id: data.txn_id,
        ssl_amount: data.amount
    }, callback);
};

MyVirtualMerchant.prototype.doVoid = function (data, callback) {
    this.processRequest({
        ssl_transaction_type: 'ccvoid',
        ssl_txn_id: data.txn_id
    }, callback);
};
<|endoftext|>


<commit_before>define(
  [
    'pex/geom/Vec2',
    'pex/geom/Vec3',
    'pex/geom/Vec4',
    'pex/geom/Mat3',
    'pex/geom/Mat4',
    'pex/geom/Quat',
    'pex/geom/Geometry',
    'pex/geom/gen',
    'pex/geom/Face3',
    'pex/geom/Face4',
    'pex/geom/Vec2Array',
    'pex/geom/Vec3Array',
    'pex/geom/Vec4Array',
    'pex/geom/Line2D',
    'pex/geom/Rect',
    'pex/geom/Triangle2D',
    'pex/geom/Polygon2D',
  ],
  function(Vec2, Vec3, Vec4, Mat3, Mat4, Quat, Geometry, gen,
    Face3, Face4, Vec2Array, Vec3Array, Vec4Array, Line2D, Rect, Triangle2D, Polygon2D) {
    return {
      Vec2 : Vec2,
      Vec3 : Vec3,
      Vec4 : Vec4,
      Mat3 : Mat3,
      Mat4 : Mat4,
      Quat : Quat,
      Geometry : Geometry,
      gen : gen,
      Face3 : Face3,
      Face4 : Face4,
      Vec2Array : Vec2Array,
      Vec3Array : Vec3Array,
      Vec4Array : Vec4Array,
      Line2D : Line2D,
      Rect : Rect,
      Triangle2D : Triangle2D,
      Polygon2D : Polygon2D
    };
  }
);

<commit_msg>Updated geom package<commit_after>define(
  [
    'pex/geom/Vec2',
    'pex/geom/Vec3',
    'pex/geom/Vec4',
    'pex/geom/Mat3',
    'pex/geom/Mat4',
    'pex/geom/Quat',
    'pex/geom/Geometry',
    'pex/geom/gen',
    'pex/geom/Face3',
    'pex/geom/Face4',
    'pex/geom/FacePolygon',
    'pex/geom/Vec2Array',
    'pex/geom/Vec3Array',
    'pex/geom/Vec4Array',
    'pex/geom/Line2D',
    'pex/geom/Rect',
    'pex/geom/Triangle2D',
    'pex/geom/Polygon2D',
    'pex/geom/hem',
    'pex/geom/BoundingBox',
    'pex/geom/Octree',
  ],
  function(Vec2, Vec3, Vec4, Mat3, Mat4, Quat, Geometry, gen,
    Face3, Face4, FacePolygon, Vec2Array, Vec3Array, Vec4Array, Line2D, Rect, Triangle2D, Polygon2D, hem,
    BoundingBox, Octree) {
    return {
      Vec2 : Vec2,
      Vec3 : Vec3,
      Vec4 : Vec4,
      Mat3 : Mat3,
      Mat4 : Mat4,
      Quat : Quat,
      Geometry : Geometry,
      gen : gen,
      Face3 : Face3,
      Face4 : Face4,
      FacePolygon : FacePolygon,
      Vec2Array : Vec2Array,
      Vec3Array : Vec3Array,
      Vec4Array : Vec4Array,
      Line2D : Line2D,
      Rect : Rect,
      Triangle2D : Triangle2D,
      Polygon2D : Polygon2D,
      hem : hem,
      BoundingBox : BoundingBox,
      Octree : Octree
    };
  }
);

<|endoftext|>


<commit_before>
var Stream = require('stream')

/*
  lemmy think...

    pause() and resume() must prevent data from being emitted.

  write controls 'drain', and the return value.

  idea:

    write -> emit('write')

    on('pause') --> writePause = true
    if(writePause) --> wirte() == false
    on('drain') --> writePause = false

    pause() -> paused = true
    resume() -> paused = false, drain()
    sendData(data) -> push onto buffer or emit.
    sendEnd()      -> queue end after buffer clears.
*/

module.exports = function (write, end) {
  var stream = new Stream()
  var buffer = [], ended = false, destroyed = false, emitEnd
  stream.writable = stream.readable = true
  stream.paused = false
  stream.buffer = buffer

  stream.writePause = false
  stream
    .on('pause', function () {
      stream.writePause = true
    })
    .on('drain', function () {
      stream.writePause = false
    })

  function destroySoon () {
    process.nextTick(stream.destroy.bind(stream))
  }

  if(write)
    stream.on('write', write)
  if(end)
    stream.on('ended', end)

  //destroy the stream once both ends are over

  stream.once('end', function () {
    stream.readable = false
    if(!stream.writable)
      stream.destroy()
  })

  stream.once('ended', function () {
    stream.writable = false
    if(!stream.readable)
      stream.destroy()
  })

  // this is the default write method,
  // if you overide it, you are resposible
  // for pause state.

  stream.emitData =
  stream.sendData = function (data) {
    if(!stream.paused && !buffer.length)
      stream.emit('data', data)
    else
      buffer.push(data)
    return !(this.paused || buffer.length)
  }

  stream.emitEnd =
  stream.sendEnd = function (data) {
    if(data) stream.write(data)
    if(emitEnd) return
    emitEnd = true
    //destroy is handled above.
    stream.drain()
  }

  stream.write = function (data) {
    stream.emit('write', data)
    return !stream.writePaused
  }
  stream.end = function () {
    stream.writable = false
    if(stream.ended) return
    stream.ended = true
    stream.emit('ended')
  }
  stream.drain = function () {
    if(!buffer.length && !emitEnd) return
    //if the stream is paused after just before emitEnd()
    //end should be buffered.
    while(!stream.paused) {
      if(buffer.length) {
        stream.emit('data', buffer.shift())
        if(buffer.length == 0)
          stream.emit('read-drain')
      }
      else if(emitEnd && stream.readable) {
        stream.readable = false
        stream.emit('end')
        return
      } return true
    }
    //if the buffer has emptied. emit drain.
  }
  var started = false
  stream.resume = function () {
    //this is where I need pauseRead, and pauseWrite.
    //here the reading side is unpaused,
    //but the writing side may still be paused.
    //the whole buffer might not empity at once.
    //it might pause again.
    //the stream should never emit data inbetween pause()...resume()
    //and write should return !buffer.length
    started = true
    stream.paused = false
    stream.drain() //will emit drain if buffer empties.
    return stream
  }

  stream.destroy = function () {
    if(destroyed) return
    destroyed = ended = true
    buffer.length = 0
    this.emit('close')
  }
  var pauseCalled = false
  stream.pause = function () {
    started = true
    stream.paused = true
    return this
  }
  stream.paused = true
  process.nextTick(function () {
    //unless the user manually
    if(started) return
    stream.resume()
  })

  return stream
}

<commit_msg>normalize this usage<commit_after>var Stream = require('stream')

/*
  lemmy think...

    pause() and resume() must prevent data from being emitted.

  write controls 'drain', and the return value.

  idea:

    write -> emit('write')

    on('pause') --> writePause = true
    if(writePause) --> wirte() == false
    on('drain') --> writePause = false

    pause() -> paused = true
    resume() -> paused = false, drain()
    sendData(data) -> push onto buffer or emit.
    sendEnd()      -> queue end after buffer clears.
*/

module.exports = function (write, end) {
  var stream = new Stream()
  var buffer = [], ended = false, destroyed = false, emitEnd
  stream.writable = stream.readable = true
  stream.paused = false
  stream.buffer = buffer

  stream.writePause = false
  stream
    .on('pause', function () {
      stream.writePause = true
    })
    .on('drain', function () {
      stream.writePause = false
    })

  function destroySoon () {
    process.nextTick(stream.destroy.bind(stream))
  }

  if(write)
    stream.on('write', write)
  if(end)
    stream.on('ended', end)

  //destroy the stream once both ends are over

  stream.once('end', function () {
    stream.readable = false
    if(!stream.writable)
      stream.destroy()
  })

  stream.once('ended', function () {
    stream.writable = false
    if(!stream.readable)
      stream.destroy()
  })

  // this is the default write method,
  // if you overide it, you are resposible
  // for pause state.

  stream.emitData =
  stream.sendData = function (data) {
    if(!stream.paused && !buffer.length)
      stream.emit('data', data)
    else
      buffer.push(data)
    return !(stream.paused || buffer.length)
  }

  stream.emitEnd =
  stream.sendEnd = function (data) {
    if(data) stream.write(data)
    if(emitEnd) return
    emitEnd = true
    //destroy is handled above.
    stream.drain()
  }

  stream.write = function (data) {
    stream.emit('write', data)
    return !stream.writePaused
  }
  stream.end = function () {
    stream.writable = false
    if(stream.ended) return
    stream.ended = true
    stream.emit('ended')
  }
  stream.drain = function () {
    if(!buffer.length && !emitEnd) return
    //if the stream is paused after just before emitEnd()
    //end should be buffered.
    while(!stream.paused) {
      if(buffer.length) {
        stream.emit('data', buffer.shift())
        if(buffer.length == 0)
          stream.emit('read-drain')
      }
      else if(emitEnd && stream.readable) {
        stream.readable = false
        stream.emit('end')
        return
      } return true
    }
    //if the buffer has emptied. emit drain.
  }
  var started = false
  stream.resume = function () {
    //this is where I need pauseRead, and pauseWrite.
    //here the reading side is unpaused,
    //but the writing side may still be paused.
    //the whole buffer might not empity at once.
    //it might pause again.
    //the stream should never emit data inbetween pause()...resume()
    //and write should return !buffer.length
    started = true
    stream.paused = false
    stream.drain() //will emit drain if buffer empties.
    return stream
  }

  stream.destroy = function () {
    if(destroyed) return
    destroyed = ended = true
    buffer.length = 0
    stream.emit('close')
  }
  var pauseCalled = false
  stream.pause = function () {
    started = true
    stream.paused = true
    return stream
  }
  stream.paused = true
  process.nextTick(function () {
    //unless the user manually
    if(started) return
    stream.resume()
  })

  return stream
}

<|endoftext|>


<commit_before>/* eslint-disable */

// Has to be written without use of Babel / ES2015+

// Taken from: https://github.com/xolvio/automated-testing-best-practices/blob/master/wallaby_server.js

// Make Babel:
// - not install the hot re-loader
// - not minify it's output to make exceptions easier to read
process.env.NODE_ENV = 'test'

// How to run:
//
// 1. Start your Meteor app and wait until it has built.
// 2. Start Wallaby with this configuration.

// How to modify for your project:
//
// 1. Change relativeMeteorAppPath (line 16 and 69) to the relative location of your Meteor app.
// 2. Adjust the files array (line 37) to match all your Meteor app server files.
// 3. Adjust the tests array (line 44) to match all your server tests that should run with Wallaby.

var path = require('path');
var fs = require('fs');
var nodePath = require('child_process')
  .execSync('meteor node -e "process.stdout.write(process.execPath)"', { encoding: 'utf8' });
var relativeMeteorAppPath = 'harness.meteor';

module.exports = function (wallaby) {
  process.env.NODE_PATH += path.delimiter +
    path.join(
      wallaby.localProjectDir,
      relativeMeteorAppPath,
      '.meteor/local/build/programs/server/node_modules'
    );

  process.env.NODE_PATH += path.delimiter +
    path.join(wallaby.localProjectDir, 'node_modules');

  // process.env.NODE_PATH += path.delimiter +
  //   path.join(wallaby.projectCacheDir, 'imports');

  return {
    files: [
      'src/**/*.js',
      '!src/**/*.spec.js',
      '!src/.meteor/**/*',
    ],

    tests: [
      'src/**/*.spec.js',
      '!src/.meteor/**/*',
    ],

    compilers: {
      '**/*.js': wallaby.compilers.babel()
    },

    env: {
      type: 'node',
      runner: nodePath,
    },

    testFramework: 'mocha',

    workers: {
      recycle: true,
    },

    debug: false,

    bootstrap: function () {
      var relativeMeteorAppPath = 'harness.meteor';

      wallaby.delayStart();

      if (!process.env.ROOT_URL) {
        process.env.ROOT_URL = 'http://localhost:2997/';
      }
      if (!process.env.MONGO_URL) {
        process.env.MONGO_URL = 'mongodb://127.0.0.1:27017/meteor-base-wallaby-server';
      }

      require('babel-polyfill')

      var path = require('path');
      var appPath = path.resolve(wallaby.localProjectDir, relativeMeteorAppPath);
      var serverPath = path.resolve(appPath,
        path.join('.meteor', 'local', 'build', 'programs', 'server')
      );
      process.argv.splice(2, 0, 'program.json');
      process.chdir(serverPath);

      //
      // boot.js
      //
      // Modifications:
      // - Only load packages

      var Fiber = require('fibers');
      var fs = require("fs");
      var path = require("path");
      var Future = require("fibers/future");
      var _ = require('underscore');
      var sourcemap_support = require('source-map-support');

      var bootUtils = require(path.resolve(serverPath, './boot-utils.js'));
      var files = require(path.resolve(serverPath, './mini-files.js'));
      var npmRequire = require(path.resolve(serverPath, './npm-require.js')).require;

      // This code is duplicated in tools/main.js.
      var MIN_NODE_VERSION = 'v0.10.41';

      if (require('semver').lt(process.version, MIN_NODE_VERSION)) {
        process.stderr.write(
          'Meteor requires Node ' + MIN_NODE_VERSION + ' or later.\n');
        process.exit(1);
      }

      // read our control files
      var serverJsonPath = path.resolve(process.argv[2]);
      var serverDir = path.dirname(serverJsonPath);
      var serverJson = require(path.resolve(serverPath, "./server-json.js"));
      var configJson =
        JSON.parse(fs.readFileSync(path.resolve(serverDir, 'config.json'), 'utf8'));

      // Set up environment
      __meteor_bootstrap__ = {
        startupHooks: [],
        serverDir: serverDir,
        configJson: configJson };
      __meteor_runtime_config__ = { meteorRelease: configJson.meteorRelease };

      if (!process.env.APP_ID) {
        process.env.APP_ID = configJson.appId;
      }

      // Map from load path to its source map.
      var parsedSourceMaps = {};

      // Read all the source maps into memory once.
      _.each(serverJson.load, function (fileInfo) {
        if (fileInfo.sourceMap) {
          var rawSourceMap = fs.readFileSync(
            path.resolve(serverDir, fileInfo.sourceMap), 'utf8');
          // Parse the source map only once, not each time it's needed. Also remove
          // the anti-XSSI header if it's there.
          var parsedSourceMap = JSON.parse(rawSourceMap.replace(/^\)\]\}'/, ''));
          // source-map-support doesn't ever look at the sourcesContent field, so
          // there's no point in keeping it in memory.
          delete parsedSourceMap.sourcesContent;
          var url;
          if (fileInfo.sourceMapRoot) {
            // Add the specified root to any root that may be in the file.
            parsedSourceMap.sourceRoot = path.join(
              fileInfo.sourceMapRoot, parsedSourceMap.sourceRoot || '');
          }
          parsedSourceMaps[path.resolve(__dirname, fileInfo.path)] = parsedSourceMap;
        }
      });

      var retrieveSourceMap = function (pathForSourceMap) {
        if (_.has(parsedSourceMaps, pathForSourceMap))
          return { map: parsedSourceMaps[pathForSourceMap] };
        return null;
      };

      var origWrapper = sourcemap_support.wrapCallSite;
      var wrapCallSite = function (frame) {
        var frame = origWrapper(frame);
        var wrapGetter = function (name) {
          var origGetter = frame[name];
          frame[name] = function (arg) {
            // replace a custom location domain that we set for better UX in Chrome
            // DevTools (separate domain group) in source maps.
            var source = origGetter(arg);
            if (! source)
              return source;
            return source.replace(/(^|\()meteor:\/\/..app\//, '$1');
          };
        };
        wrapGetter('getScriptNameOrSourceURL');
        wrapGetter('getEvalOrigin');

        return frame;
      };
      sourcemap_support.install({
        // Use the source maps specified in program.json instead of parsing source
        // code for them.
        retrieveSourceMap: retrieveSourceMap,
        // For now, don't fix the source line in uncaught exceptions, because we
        // haven't fixed handleUncaughtExceptions in source-map-support to properly
        // locate the source files.
        handleUncaughtExceptions: false,
        wrapCallSite: wrapCallSite
      });

      // Only enabled by default in development.
      if (process.env.METEOR_SHELL_DIR) {
        require(path.resolve(serverPath, './shell-server.js')).listen(process.env.METEOR_SHELL_DIR);
      }

      // As a replacement to the old keepalives mechanism, check for a running
      // parent every few seconds. Exit if the parent is not running.
      //
      // Two caveats to this strategy:
      // * Doesn't catch the case where the parent is CPU-hogging (but maybe we
      //   don't want to catch that case anyway, since the bundler not yielding
      //   is what caused #2536).
      // * Could be fooled by pid re-use, i.e. if another process comes up and
      //   takes the parent process's place before the child process dies.
      var startCheckForLiveParent = function (parentPid) {
        if (parentPid) {
          if (! bootUtils.validPid(parentPid)) {
            console.error("METEOR_PARENT_PID must be a valid process ID.");
            process.exit(1);
          }

          setInterval(function () {
            try {
              process.kill(parentPid, 0);
            } catch (err) {
              console.error("Parent process is dead! Exiting.");
              process.exit(1);
            }
          }, 3000);
        }
      };

      Fiber(function () {
        _.each(serverJson.load, function (fileInfo) {
          // Modification: Only load packages
          if (fileInfo.path.indexOf('packages/') !== 0) {
            return;
          }

          var code = fs.readFileSync(path.resolve(serverDir, fileInfo.path));
          var nonLocalNodeModulesPaths = [];

          function addNodeModulesPath(path) {
            nonLocalNodeModulesPaths.push(
              files.pathResolve(serverDir, path)
            );
          }

          if (typeof fileInfo.node_modules === "string") {
            addNodeModulesPath(fileInfo.node_modules);
          } else if (fileInfo.node_modules) {
            _.each(fileInfo.node_modules, function (info, path) {
              if (! info.local) {
                addNodeModulesPath(path);
              }
            });
          }

          var Npm = {
            /**
             * @summary Require a package that was specified using
             * `Npm.depends()`.
             * @param  {String} name The name of the package to require.
             * @locus Server
             * @memberOf Npm
             */
            require: function (name) {
              if (nonLocalNodeModulesPaths.length === 0) {
                return require(name);
              }

              var fullPath;

              nonLocalNodeModulesPaths.some(function (nodeModuleBase) {
                var packageBase = files.convertToOSPath(files.pathResolve(
                  nodeModuleBase,
                  name.split("/", 1)[0]
                ));

                if (fs.existsSync(packageBase)) {
                  return fullPath = files.convertToOSPath(
                    files.pathResolve(nodeModuleBase, name)
                  );
                }
              });

              if (fullPath) {
                return require(fullPath);
              }

              try {
                return require(name);
              } catch (e) {
                // Try to guess the package name so we can print a nice
                // error message
                // fileInfo.path is a standard path, use files.pathSep
                var filePathParts = fileInfo.path.split(files.pathSep);
                var packageName = filePathParts[1].replace(/\.js$/, '');

                // XXX better message
                throw new Error(
                  "Can't find npm module '" + name +
                  "'. Did you forget to call 'Npm.depends' in package.js " +
                  "within the '" + packageName + "' package?");
              }
            }
          };
          var getAsset = function (assetPath, encoding, callback) {
            var fut;
            if (! callback) {
              fut = new Future();
              callback = fut.resolver();
            }
            // This assumes that we've already loaded the meteor package, so meteor
            // itself can't call Assets.get*. (We could change this function so that
            // it doesn't call bindEnvironment if you don't pass a callback if we need
            // to.)
            var _callback = Package.meteor.Meteor.bindEnvironment(function (err, result) {
              if (result && ! encoding)
              // Sadly, this copies in Node 0.10.
                result = new Uint8Array(result);
              callback(err, result);
            }, function (e) {
              console.log("Exception in callback of getAsset", e.stack);
            });

            // Convert a DOS-style path to Unix-style in case the application code was
            // written on Windows.
            assetPath = files.convertToStandardPath(assetPath);

            if (!fileInfo.assets || !_.has(fileInfo.assets, assetPath)) {
              _callback(new Error("Unknown asset: " + assetPath));
            } else {
              var filePath = path.join(serverDir, fileInfo.assets[assetPath]);
              fs.readFile(files.convertToOSPath(filePath), encoding, _callback);
            }
            if (fut)
              return fut.wait();
          };

          var Assets = {
            getText: function (assetPath, callback) {
              return getAsset(assetPath, "utf8", callback);
            },
            getBinary: function (assetPath, callback) {
              return getAsset(assetPath, undefined, callback);
            },
            absoluteFilePath: function (assetPath) {
              if (!fileInfo.assets || !_.has(fileInfo.assets, assetPath)) {
                throw new Error("Unknown asset: " + assetPath);
              }

              assetPath = files.convertToStandardPath(assetPath);
              var filePath = path.join(serverDir, fileInfo.assets[assetPath]);
              return files.convertToOSPath(filePath);
            },
          };

          var isModulesRuntime =
            fileInfo.path === "packages/modules-runtime.js";

          var wrapParts = ["(function(Npm,Assets"];
          if (isModulesRuntime) {
            wrapParts.push(",npmRequire");
          }
          // \n is necessary in case final line is a //-comment
          wrapParts.push("){", code, "\n})");
          var wrapped = wrapParts.join("");

          // It is safer to use the absolute path when source map is present as
          // different tooling, such as node-inspector, can get confused on relative
          // urls.

          // fileInfo.path is a standard path, convert it to OS path to join with
          // __dirname
          var fileInfoOSPath = files.convertToOSPath(fileInfo.path);
          var absoluteFilePath = path.resolve(__dirname, fileInfoOSPath);

          var scriptPath =
            parsedSourceMaps[absoluteFilePath] ? absoluteFilePath : fileInfoOSPath;
          // The final 'true' is an undocumented argument to runIn[Foo]Context that
          // causes it to print out a descriptive error message on parse error. It's
          // what require() uses to generate its errors.
          var func = require('vm').runInThisContext(wrapped, scriptPath, true);
          var args = [Npm, Assets];
          if (isModulesRuntime) {
            args.push(npmRequire);
          }
          func.apply(global, args);
        });

        //// run the user startup hooks.  other calls to startup() during this can still
        //// add hooks to the end.
        //while (__meteor_bootstrap__.startupHooks.length) {
        //  var hook = __meteor_bootstrap__.startupHooks.shift();
        //  hook();
        //}
        //// Setting this to null tells Meteor.startup to call hooks immediately.
        //__meteor_bootstrap__.startupHooks = null;
        //
        //// find and run main()
        //// XXX hack. we should know the package that contains main.
        //var mains = [];
        //var globalMain;
        //if ('main' in global) {
        //  mains.push(main);
        //  globalMain = main;
        //}
        //typeof Package !== 'undefined' && _.each(Package, function (p, n) {
        //  if ('main' in p && p.main !== globalMain) {
        //    mains.push(p.main);
        //  }
        //});
        //if (! mains.length) {
        //  process.stderr.write("Program has no main() function.\n");
        //  process.exit(1);
        //}
        //if (mains.length > 1) {
        //  process.stderr.write("Program has more than one main() function?\n");
        //  process.exit(1);
        //}
        //var exitCode = mains[0].call({}, process.argv.slice(3));
        //// XXX hack, needs a better way to keep alive
        //if (exitCode !== 'DAEMON')
        //  process.exit(exitCode);
        //
        //if (process.env.METEOR_PARENT_PID) {
        //  startCheckForLiveParent(process.env.METEOR_PARENT_PID);
        //}

        process.chdir(appPath);
        wallaby.start();
      }).run();
    },
  };
};

<commit_msg>Wallaby report unhandledRejections<commit_after>/* eslint-disable */

// Has to be written without use of Babel / ES2015+

// Taken from: https://github.com/xolvio/automated-testing-best-practices/blob/master/wallaby_server.js

// Make Babel:
// - not install the hot re-loader
// - not minify it's output to make exceptions easier to read
process.env.NODE_ENV = 'test'

// How to run:
//
// 1. Start your Meteor app and wait until it has built.
// 2. Start Wallaby with this configuration.

// How to modify for your project:
//
// 1. Change relativeMeteorAppPath (line 16 and 69) to the relative location of your Meteor app.
// 2. Adjust the files array (line 37) to match all your Meteor app server files.
// 3. Adjust the tests array (line 44) to match all your server tests that should run with Wallaby.

var path = require('path');
var fs = require('fs');
var nodePath = require('child_process')
  .execSync('meteor node -e "process.stdout.write(process.execPath)"', { encoding: 'utf8' });
var relativeMeteorAppPath = 'harness.meteor';

module.exports = function (wallaby) {
  process.env.NODE_PATH += path.delimiter +
    path.join(
      wallaby.localProjectDir,
      relativeMeteorAppPath,
      '.meteor/local/build/programs/server/node_modules'
    );

  process.env.NODE_PATH += path.delimiter +
    path.join(wallaby.localProjectDir, 'node_modules');

  // process.env.NODE_PATH += path.delimiter +
  //   path.join(wallaby.projectCacheDir, 'imports');

  return {
    files: [
      'src/**/*.js',
      '!src/**/*.spec.js',
      '!src/.meteor/**/*',
    ],

    tests: [
      'src/**/*.spec.js',
      '!src/.meteor/**/*',
    ],

    compilers: {
      '**/*.js': wallaby.compilers.babel()
    },

    env: {
      type: 'node',
      runner: nodePath,
    },

    testFramework: 'mocha',

    workers: {
      recycle: true,
    },

    debug: false,

    bootstrap: function () {
      var relativeMeteorAppPath = 'harness.meteor';

      wallaby.delayStart();

      if (!process.env.ROOT_URL) {
        process.env.ROOT_URL = 'http://localhost:2997/';
      }
      if (!process.env.MONGO_URL) {
        process.env.MONGO_URL = 'mongodb://127.0.0.1:27017/meteor-base-wallaby-server';
      }

      process.on('unhandledRejection', (reason, promise) => {
        console.log('Unhandled promise rejection', reason)
      })

      var path = require('path');
      var appPath = path.resolve(wallaby.localProjectDir, relativeMeteorAppPath);
      var serverPath = path.resolve(appPath,
        path.join('.meteor', 'local', 'build', 'programs', 'server')
      );
      process.argv.splice(2, 0, 'program.json');
      process.chdir(serverPath);

      //
      // boot.js
      //
      // Modifications:
      // - Only load packages

      var Fiber = require('fibers');
      var fs = require("fs");
      var path = require("path");
      var Future = require("fibers/future");
      var _ = require('underscore');
      var sourcemap_support = require('source-map-support');

      var bootUtils = require(path.resolve(serverPath, './boot-utils.js'));
      var files = require(path.resolve(serverPath, './mini-files.js'));
      var npmRequire = require(path.resolve(serverPath, './npm-require.js')).require;

      // This code is duplicated in tools/main.js.
      var MIN_NODE_VERSION = 'v0.10.41';

      if (require('semver').lt(process.version, MIN_NODE_VERSION)) {
        process.stderr.write(
          'Meteor requires Node ' + MIN_NODE_VERSION + ' or later.\n');
        process.exit(1);
      }

      // read our control files
      var serverJsonPath = path.resolve(process.argv[2]);
      var serverDir = path.dirname(serverJsonPath);
      var serverJson = require(path.resolve(serverPath, "./server-json.js"));
      var configJson =
        JSON.parse(fs.readFileSync(path.resolve(serverDir, 'config.json'), 'utf8'));

      // Set up environment
      __meteor_bootstrap__ = {
        startupHooks: [],
        serverDir: serverDir,
        configJson: configJson };
      __meteor_runtime_config__ = { meteorRelease: configJson.meteorRelease };

      if (!process.env.APP_ID) {
        process.env.APP_ID = configJson.appId;
      }

      // Map from load path to its source map.
      var parsedSourceMaps = {};

      // Read all the source maps into memory once.
      _.each(serverJson.load, function (fileInfo) {
        if (fileInfo.sourceMap) {
          var rawSourceMap = fs.readFileSync(
            path.resolve(serverDir, fileInfo.sourceMap), 'utf8');
          // Parse the source map only once, not each time it's needed. Also remove
          // the anti-XSSI header if it's there.
          var parsedSourceMap = JSON.parse(rawSourceMap.replace(/^\)\]\}'/, ''));
          // source-map-support doesn't ever look at the sourcesContent field, so
          // there's no point in keeping it in memory.
          delete parsedSourceMap.sourcesContent;
          var url;
          if (fileInfo.sourceMapRoot) {
            // Add the specified root to any root that may be in the file.
            parsedSourceMap.sourceRoot = path.join(
              fileInfo.sourceMapRoot, parsedSourceMap.sourceRoot || '');
          }
          parsedSourceMaps[path.resolve(__dirname, fileInfo.path)] = parsedSourceMap;
        }
      });

      var retrieveSourceMap = function (pathForSourceMap) {
        if (_.has(parsedSourceMaps, pathForSourceMap))
          return { map: parsedSourceMaps[pathForSourceMap] };
        return null;
      };

      var origWrapper = sourcemap_support.wrapCallSite;
      var wrapCallSite = function (frame) {
        var frame = origWrapper(frame);
        var wrapGetter = function (name) {
          var origGetter = frame[name];
          frame[name] = function (arg) {
            // replace a custom location domain that we set for better UX in Chrome
            // DevTools (separate domain group) in source maps.
            var source = origGetter(arg);
            if (! source)
              return source;
            return source.replace(/(^|\()meteor:\/\/..app\//, '$1');
          };
        };
        wrapGetter('getScriptNameOrSourceURL');
        wrapGetter('getEvalOrigin');

        return frame;
      };
      sourcemap_support.install({
        // Use the source maps specified in program.json instead of parsing source
        // code for them.
        retrieveSourceMap: retrieveSourceMap,
        // For now, don't fix the source line in uncaught exceptions, because we
        // haven't fixed handleUncaughtExceptions in source-map-support to properly
        // locate the source files.
        handleUncaughtExceptions: false,
        wrapCallSite: wrapCallSite
      });

      // Only enabled by default in development.
      if (process.env.METEOR_SHELL_DIR) {
        require(path.resolve(serverPath, './shell-server.js')).listen(process.env.METEOR_SHELL_DIR);
      }

      // As a replacement to the old keepalives mechanism, check for a running
      // parent every few seconds. Exit if the parent is not running.
      //
      // Two caveats to this strategy:
      // * Doesn't catch the case where the parent is CPU-hogging (but maybe we
      //   don't want to catch that case anyway, since the bundler not yielding
      //   is what caused #2536).
      // * Could be fooled by pid re-use, i.e. if another process comes up and
      //   takes the parent process's place before the child process dies.
      var startCheckForLiveParent = function (parentPid) {
        if (parentPid) {
          if (! bootUtils.validPid(parentPid)) {
            console.error("METEOR_PARENT_PID must be a valid process ID.");
            process.exit(1);
          }

          setInterval(function () {
            try {
              process.kill(parentPid, 0);
            } catch (err) {
              console.error("Parent process is dead! Exiting.");
              process.exit(1);
            }
          }, 3000);
        }
      };

      Fiber(function () {
        _.each(serverJson.load, function (fileInfo) {
          // Modification: Only load packages
          if (fileInfo.path.indexOf('packages/') !== 0) {
            return;
          }

          var code = fs.readFileSync(path.resolve(serverDir, fileInfo.path));
          var nonLocalNodeModulesPaths = [];

          function addNodeModulesPath(path) {
            nonLocalNodeModulesPaths.push(
              files.pathResolve(serverDir, path)
            );
          }

          if (typeof fileInfo.node_modules === "string") {
            addNodeModulesPath(fileInfo.node_modules);
          } else if (fileInfo.node_modules) {
            _.each(fileInfo.node_modules, function (info, path) {
              if (! info.local) {
                addNodeModulesPath(path);
              }
            });
          }

          var Npm = {
            /**
             * @summary Require a package that was specified using
             * `Npm.depends()`.
             * @param  {String} name The name of the package to require.
             * @locus Server
             * @memberOf Npm
             */
            require: function (name) {
              if (nonLocalNodeModulesPaths.length === 0) {
                return require(name);
              }

              var fullPath;

              nonLocalNodeModulesPaths.some(function (nodeModuleBase) {
                var packageBase = files.convertToOSPath(files.pathResolve(
                  nodeModuleBase,
                  name.split("/", 1)[0]
                ));

                if (fs.existsSync(packageBase)) {
                  return fullPath = files.convertToOSPath(
                    files.pathResolve(nodeModuleBase, name)
                  );
                }
              });

              if (fullPath) {
                return require(fullPath);
              }

              try {
                return require(name);
              } catch (e) {
                // Try to guess the package name so we can print a nice
                // error message
                // fileInfo.path is a standard path, use files.pathSep
                var filePathParts = fileInfo.path.split(files.pathSep);
                var packageName = filePathParts[1].replace(/\.js$/, '');

                // XXX better message
                throw new Error(
                  "Can't find npm module '" + name +
                  "'. Did you forget to call 'Npm.depends' in package.js " +
                  "within the '" + packageName + "' package?");
              }
            }
          };
          var getAsset = function (assetPath, encoding, callback) {
            var fut;
            if (! callback) {
              fut = new Future();
              callback = fut.resolver();
            }
            // This assumes that we've already loaded the meteor package, so meteor
            // itself can't call Assets.get*. (We could change this function so that
            // it doesn't call bindEnvironment if you don't pass a callback if we need
            // to.)
            var _callback = Package.meteor.Meteor.bindEnvironment(function (err, result) {
              if (result && ! encoding)
              // Sadly, this copies in Node 0.10.
                result = new Uint8Array(result);
              callback(err, result);
            }, function (e) {
              console.log("Exception in callback of getAsset", e.stack);
            });

            // Convert a DOS-style path to Unix-style in case the application code was
            // written on Windows.
            assetPath = files.convertToStandardPath(assetPath);

            if (!fileInfo.assets || !_.has(fileInfo.assets, assetPath)) {
              _callback(new Error("Unknown asset: " + assetPath));
            } else {
              var filePath = path.join(serverDir, fileInfo.assets[assetPath]);
              fs.readFile(files.convertToOSPath(filePath), encoding, _callback);
            }
            if (fut)
              return fut.wait();
          };

          var Assets = {
            getText: function (assetPath, callback) {
              return getAsset(assetPath, "utf8", callback);
            },
            getBinary: function (assetPath, callback) {
              return getAsset(assetPath, undefined, callback);
            },
            absoluteFilePath: function (assetPath) {
              if (!fileInfo.assets || !_.has(fileInfo.assets, assetPath)) {
                throw new Error("Unknown asset: " + assetPath);
              }

              assetPath = files.convertToStandardPath(assetPath);
              var filePath = path.join(serverDir, fileInfo.assets[assetPath]);
              return files.convertToOSPath(filePath);
            },
          };

          var isModulesRuntime =
            fileInfo.path === "packages/modules-runtime.js";

          var wrapParts = ["(function(Npm,Assets"];
          if (isModulesRuntime) {
            wrapParts.push(",npmRequire");
          }
          // \n is necessary in case final line is a //-comment
          wrapParts.push("){", code, "\n})");
          var wrapped = wrapParts.join("");

          // It is safer to use the absolute path when source map is present as
          // different tooling, such as node-inspector, can get confused on relative
          // urls.

          // fileInfo.path is a standard path, convert it to OS path to join with
          // __dirname
          var fileInfoOSPath = files.convertToOSPath(fileInfo.path);
          var absoluteFilePath = path.resolve(__dirname, fileInfoOSPath);

          var scriptPath =
            parsedSourceMaps[absoluteFilePath] ? absoluteFilePath : fileInfoOSPath;
          // The final 'true' is an undocumented argument to runIn[Foo]Context that
          // causes it to print out a descriptive error message on parse error. It's
          // what require() uses to generate its errors.
          var func = require('vm').runInThisContext(wrapped, scriptPath, true);
          var args = [Npm, Assets];
          if (isModulesRuntime) {
            args.push(npmRequire);
          }
          func.apply(global, args);
        });

        //// run the user startup hooks.  other calls to startup() during this can still
        //// add hooks to the end.
        //while (__meteor_bootstrap__.startupHooks.length) {
        //  var hook = __meteor_bootstrap__.startupHooks.shift();
        //  hook();
        //}
        //// Setting this to null tells Meteor.startup to call hooks immediately.
        //__meteor_bootstrap__.startupHooks = null;
        //
        //// find and run main()
        //// XXX hack. we should know the package that contains main.
        //var mains = [];
        //var globalMain;
        //if ('main' in global) {
        //  mains.push(main);
        //  globalMain = main;
        //}
        //typeof Package !== 'undefined' && _.each(Package, function (p, n) {
        //  if ('main' in p && p.main !== globalMain) {
        //    mains.push(p.main);
        //  }
        //});
        //if (! mains.length) {
        //  process.stderr.write("Program has no main() function.\n");
        //  process.exit(1);
        //}
        //if (mains.length > 1) {
        //  process.stderr.write("Program has more than one main() function?\n");
        //  process.exit(1);
        //}
        //var exitCode = mains[0].call({}, process.argv.slice(3));
        //// XXX hack, needs a better way to keep alive
        //if (exitCode !== 'DAEMON')
        //  process.exit(exitCode);
        //
        //if (process.env.METEOR_PARENT_PID) {
        //  startCheckForLiveParent(process.env.METEOR_PARENT_PID);
        //}

        process.chdir(appPath);
        wallaby.start();
      }).run();
    },
  };
};

<|endoftext|>