<commit_before>//
// # RequestEmitter
//

//
// ## Error Handling
//
// All of the error handling for RequestEmitters is facilitated by the callback functions its methods accept.
//
var EventEmitter = require('events').EventEmitter
  , url = require('url')
  , debug = require('debug')('shuttle:RequestEmitter')
  , zmqstream = require('zmq-stream')

//
// ## RequestEmitter `RequestEmitter(options)`
//
// Creates a new RequestEmitter with the specified options:
//
//  * `timeout`: A duration, in ms, that the RequestEmitter will wait for a response before retrying or raising an
//  Error. If zero, no timeout mechanism will be used. If requests can not be guaranteed to be idempotent, `timeout` and
//  `retries` should not be used. Defaults to 0 to keep from unduly making this guarantee.
//  * `retries`: A number of times that the RequestEmitter will automatically retry a request that has timed out before
//  raising an Error. If zero, no retries will be made, even if `timeout` is defined. If requests can not be guaranteed
//  to be idempotent, `timeout` and `retries` should not be used. Defaults to 3, which is an arbitrarily-chosen number.
//  * `linger`: A duration, in ms, that the RequestEmitter will wait for outgoing messages to be sent before releasing
//  its resources after `close` is called. Outgoing messages take a non-zero time to be completely sent, and can be
//  dropped by a subsequent call to `close`. A value of -1 indicates an infinite delay. Defaults to 0.
//
function RequestEmitter(obj) {
  obj = obj || {}

  this.linger = (typeof obj.linger === 'number') ? obj.linger : 0

  this._nextRequestId = 0
  this._pending = {}
  this._zdealer = null
}

//
// ## emit `emit(name, data, callback)`
//
// Emits an event (request) named **name**, along with **data**, over the Shuttle mesh, to be handled by some
// RequestHandler. If successful, **Callback** will receive the result of the request. Otherwise, **callback** will
// receive the error.
//
// If the request times out (as defined by `RequestEmitter.timeout` and `RequestEmitter.retries`), **callback** will
// receive a TimeoutError.
//
// NOTE: Do not try to call other EventEmitter methods like `on`. It won't work.
//
// Returns the RequestEmitter.
//
RequestEmitter.prototype.emit = emit
function emit(name, data, callback) {
  var self = this
    , requestId
    , payload

  if (name === 'newListener') {
    return EventEmitter.prototype.emit.call(this, name, data)
  }

  requestId = String(self._nextRequestId++)

  self._pending[requestId] = callback
  payload = JSON.stringify({
    name: name,
    data: data
  })

  debug('Emitting %s as request %s with %s.', name, requestId, payload)

  if (!self._zdealer.write([
    new Buffer(0),
    new Buffer(requestId),
    new Buffer(payload)
  ])) {
    // TODO: Bubble up the internal 'drain' event?
    // TODO: Put some place safe.
    callback(new Error('Too many requests'))
  }

  return self
}

//
// ## listen `listen(options)`
//
// ### Also `listenForRequests`
//
// Synchronously listens for RequestHandler connections. If **options.url** is provided, that URL will be used.
// Otherwise, **options** will be formatted as a URL as defined by the core `url` module.
//
// Returns the RequestEmitter.
//
RequestEmitter.prototype.listenForRequests = listen
RequestEmitter.prototype.listen = listen
function listen(options) {
  var self = this
    , opts = options || {}
    , iface = options.url

  if (!iface) {
    iface = url.format(opts)
  }

  debug('Listening to %s.', iface)

  self._initSocket()
  self._zdealer.bind(iface)

  return self
}

//
// ## connect `connect(options)`
//
// ### Also `connectForRequests`
//
// Synchronously connects to a listening RequestHandler. If **options.url** is provided, that URL will be used.
// Otherwise, **options** will be formatted as a URL as defined by the core `url` module.
//
// Returns the RequestEmitter.
//
RequestEmitter.prototype.connectForRequests = connect
RequestEmitter.prototype.connect = connect
function connect(options) {
  var self = this
    , opts = options || {}
    , iface = options.url

  if (!iface) {
    iface = url.format(opts)
  }

  debug('Connecting to %s.', iface)

  self._initSocket()
  self._zdealer.connect(iface)

  return self
}

//
// ## close `close()`
//
// Synchonously releases the underlying resources, allowing the RequestEmitter to be `connect`ed or `listen`ed again
// freely.
//
// Unless the RequestEmitter was configured with a `linger` period, all pending outgoing messages will be dropped.
//
// Returns the RequestEmitter.
//
RequestEmitter.prototype.close = close
function close() {
  var self = this

  if (!self._zdealer) {
    return
  }

  debug('Closing.')

  self._zdealer.close()
  self._zdealer = null

  return self
}

//
// ## _initSocket `_initSocket()`
//
// Internal use only.
//
// Creates the underlying networking resources.
//
RequestEmitter.prototype._initSocket = _initSocket
function _initSocket() {
  var self = this

  if (self._zdealer) {
    return
  }

  self._zdealer = new zmqstream.Socket({
    type: zmqstream.Type.DEALER
  })

  self._zdealer.set(zmqstream.Option.LINGER, self.linger)

  self._zdealer.on('readable', function () {
    self._handleResponses()
  })
  self._handleResponses()
}

//
// ## _handleResponses `_handleResponses()`
//
// Internal use only.
//
// Polls the network for requests, re-emitting them locally for handling.
//
RequestEmitter.prototype._handleResponses = _handleResponses
function _handleResponses() {
  var self = this
    , messages = null

  // 1. If the dealer socket is currently closed, it cannot be read from. Otherwise, read from it.
  if (!self._zdealer) {
    return
  }

  messages = self._zdealer.read(10)

  // 1. If there are no messages, we'll come back to this later. For now, just leave.
  if (!messages) {
    return
  }

  // 1. For each message, we want to look up the callback and fire it.
  messages.forEach(function (envelope) {
    var payload
      , requestId

    //    1. If we don't have three message frames or the body isn't valid JSON, this didn't come from a
    //    RequestHandler. It's safe to ignore.
    if (envelope.length !== 3) {
      return
    }

    try {
      payload = JSON.parse(envelope.pop().toString('utf8'))
    } catch (e) {
      return
    }

    requestId = String(envelope.pop())

    //    1. If we don't have a callback function, we assume it's meant to be a fire-and-forget event, so we'll ignore
    //    the response.
    if (!self._pending[requestId]) {
      return
    }

    debug('Handling response to request %s with (%s, %s).', requestId, payload.err, JSON.stringify(payload.data))

    self._pending[requestId](payload.err, payload.data)
    ;delete self._pending[requestId]
  })

  // 1. We may have more messages to receive, so try reading again soon.
  process.nextTick(function () {
    self._handleResponses()
  })
}

module.exports = RequestEmitter
<commit_msg>Implemented timeout and retry.<commit_after>//
// # RequestEmitter
//

//
// ## Error Handling
//
// All of the error handling for RequestEmitters is facilitated by the callback functions its methods accept.
//
var EventEmitter = require('events').EventEmitter
  , url = require('url')
  , debug = require('debug')('shuttle:RequestEmitter')
  , zmqstream = require('zmq-stream')

//
// ## RequestEmitter `RequestEmitter(options)`
//
// Creates a new RequestEmitter with the specified options:
//
//  * `timeout`: A duration, in ms, that the RequestEmitter will wait for a response before retrying or raising an
//  Error. If zero, no timeout mechanism will be used. If requests can not be guaranteed to be idempotent, `timeout` and
//  `retries` should not be used. Defaults to 0 to keep from unduly making this guarantee.
//  * `retries`: A number of times that the RequestEmitter will automatically retry a request that has timed out before
//  raising an Error. If zero, no retries will be made, even if `timeout` is defined. If requests can not be guaranteed
//  to be idempotent, `timeout` and `retries` should not be used. Defaults to 3, which is an arbitrarily-chosen number.
//  * `linger`: A duration, in ms, that the RequestEmitter will wait for outgoing messages to be sent before releasing
//  its resources after `close` is called. Outgoing messages take a non-zero time to be completely sent, and can be
//  dropped by a subsequent call to `close`. A value of -1 indicates an infinite delay. Defaults to 0.
//
function RequestEmitter(obj) {
  obj = obj || {}

  this.timeout = (typeof obj.timeout === 'number') ? obj.timeout : 0
  this.retries = (typeof obj.retries === 'number') ? obj.timeout : 3
  this.linger = (typeof obj.linger === 'number') ? obj.linger : 0

  this._nextRequestId = 0
  this._pending = {}
  this._zdealer = null
}

//
// ## emit `emit(name, data, callback)`
//
// Emits an event (request) named **name**, along with **data**, over the Shuttle mesh, to be handled by some
// RequestHandler. If successful, **Callback** will receive the result of the request. Otherwise, **callback** will
// receive the error.
//
// If the request times out (as defined by `RequestEmitter.timeout` and `RequestEmitter.retries`), **callback** will
// receive a TimeoutError.
//
// NOTE: Do not try to call other EventEmitter methods like `on`. It won't work.
//
// Returns the RequestEmitter.
//
RequestEmitter.prototype.emit = emit
function emit(name, data, callback) {
  var self = this

  if (name === 'newListener') {
    return EventEmitter.prototype.emit.call(this, name, data)
  }

  self._emitAndRetry(name, data, callback, self.retries)

  return self
}

//
// ## listen `listen(options)`
//
// ### Also `listenForRequests`
//
// Synchronously listens for RequestHandler connections. If **options.url** is provided, that URL will be used.
// Otherwise, **options** will be formatted as a URL as defined by the core `url` module.
//
// Returns the RequestEmitter.
//
RequestEmitter.prototype.listenForRequests = listen
RequestEmitter.prototype.listen = listen
function listen(options) {
  var self = this
    , opts = options || {}
    , iface = options.url

  if (!iface) {
    iface = url.format(opts)
  }

  debug('Listening to %s.', iface)

  self._initSocket()
  self._zdealer.bind(iface)

  return self
}

//
// ## connect `connect(options)`
//
// ### Also `connectForRequests`
//
// Synchronously connects to a listening RequestHandler. If **options.url** is provided, that URL will be used.
// Otherwise, **options** will be formatted as a URL as defined by the core `url` module.
//
// Returns the RequestEmitter.
//
RequestEmitter.prototype.connectForRequests = connect
RequestEmitter.prototype.connect = connect
function connect(options) {
  var self = this
    , opts = options || {}
    , iface = options.url

  if (!iface) {
    iface = url.format(opts)
  }

  debug('Connecting to %s.', iface)

  self._initSocket()
  self._zdealer.connect(iface)

  return self
}

//
// ## close `close()`
//
// Synchonously releases the underlying resources, allowing the RequestEmitter to be `connect`ed or `listen`ed again
// freely.
//
// Unless the RequestEmitter was configured with a `linger` period, all pending outgoing messages will be dropped.
//
// Returns the RequestEmitter.
//
RequestEmitter.prototype.close = close
function close() {
  var self = this

  if (!self._zdealer) {
    return
  }

  debug('Closing.')

  self._zdealer.close()
  self._zdealer = null

  return self
}

//
// ## _initSocket `_initSocket()`
//
// Internal use only.
//
// Creates the underlying networking resources.
//
RequestEmitter.prototype._initSocket = _initSocket
function _initSocket() {
  var self = this

  if (self._zdealer) {
    return
  }

  self._zdealer = new zmqstream.Socket({
    type: zmqstream.Type.DEALER
  })

  self._zdealer.set(zmqstream.Option.LINGER, self.linger)

  self._zdealer.on('readable', function () {
    self._handleResponses()
  })
  self._handleResponses()
}

//
// ## _emitAndRetry `_emitAndRetry(name, data, callback, retries)`
//
// Internal use only.
//
// Sends a request across the network, retrying at most **retries** times.
//
RequestEmitter.prototype._emitAndRetry = _emitAndRetry
function _emitAndRetry(name, data, callback, retries) {
  var self = this
    , requestId = String(self._nextRequestId++)
    , payload
    , success

  self._pending[requestId] = {
    callback: (typeof callback === 'function') ? callback : null,
    timerId: null
  }

  payload = JSON.stringify({
    name: name,
    data: data
  })

  debug('Emitting %s as request %s with %s.', name, requestId, payload)

  success = self._zdealer.write([
    new Buffer(0),
    new Buffer(requestId),
    new Buffer(payload)
  ])

  if (typeof callback === 'function') {
    if (!success) {
      // TODO: Bubble up the internal 'drain' event?
      // TODO: Put some place safe.
      callback(new Error('Too many requests'))
      return
    }

    if (self.timeout > 0) {
      self._pending[requestId].timerId = setTimeout(
        function timeout() {
          debug('Timeout during request %s named %s.', requestId, name)

          ;delete self._pending[requestId]

          if (retries > 0) {
            self._emitAndRetry(name, data, callback, retries - 1)
          } else {
            callback(new Error('Timeout'))
          }
        },
        self.timeout
      )
    }
  }}

//
// ## _handleResponses `_handleResponses()`
//
// Internal use only.
//
// Polls the network for requests, re-emitting them locally for handling.
//
RequestEmitter.prototype._handleResponses = _handleResponses
function _handleResponses() {
  var self = this
    , messages = null
    , pending = null

  // 1. If the dealer socket is currently closed, it cannot be read from. Otherwise, read from it.
  if (!self._zdealer) {
    return
  }

  messages = self._zdealer.read(10)

  // 1. If there are no messages, we'll come back to this later. For now, just leave.
  if (!messages) {
    return
  }

  // 1. For each message, we want to look up the callback and fire it.
  messages.forEach(function (envelope) {
    var payload
      , requestId

    //    1. If we don't have three message frames or the body isn't valid JSON, this didn't come from a
    //    RequestHandler. It's safe to ignore.
    if (envelope.length !== 3) {
      return
    }

    try {
      payload = JSON.parse(envelope.pop().toString('utf8'))
    } catch (e) {
      return
    }

    requestId = String(envelope.pop())
    pending = self._pending[requestId]

    //    1. If we don't have a callback function, we assume it's meant to be a fire-and-forget event, so we'll ignore
    //    the response.
    if (!pending || !pending.callback) {
      return
    }

    debug('Handling response to request %s with (%s, %s).', requestId, payload.err, JSON.stringify(payload.data))

    clearTimeout(pending.timerId)
    pending.callback(payload.err, payload.data)
    ;delete self._pending[requestId]
  })

  // 1. We may have more messages to receive, so try reading again soon.
  process.nextTick(function () {
    self._handleResponses()
  })
}

module.exports = RequestEmitter
<|endoftext|>


<commit_before>module.exports = function BlastRequestBrowser(Blast, Collection) {

	var Request = Blast.Classes.Develry.Request,
	    original_send = XMLHttpRequest.prototype.send,
	    original_open = XMLHttpRequest.prototype.open,
	    START_SYMBOL = Symbol('start');

	/**
	 * Actually make the request
	 *
	 * @author   Jelle De Loecker   <jelle@develry.be>
	 * @since    0.6.2
	 * @version  0.7.0
	 *
	 * @return   {Pledge}
	 */
	Request.setMethod(function _make_request() {

		var that = this,
		    pledge = new Pledge(),
		    method = this.method_info,
		    temp_timeout,
		    finished,
		    is_form,
		    timeout,
		    result,
		    error,
		    body = this.body,
		    bomb,
		    type,
		    key,
		    xhr;

		if (this.get) {
			this.url.addQuery(this.get);
		}

		if (this.cache === false) {
			this.url.param('_', this.time_started);
		} else {
			this.url.param('_', null);
		}

		// Create the request
		xhr = new XMLHttpRequest();
		this.xhr = xhr;

		// Allow cancelling the request
		pledge.cancel = function cancel() {
			xhr.abort();
		};

		// DNS failures or no available connection will cause this error
		xhr.addEventListener('error', function onError(event) {

			error = new Error('Transfer failed');

			// Simulate a 408 "timeout"
			error.status = error.number = 408;

			Blast.state.reportError(error);

			done();
		}, false);

		// Catch aborts
		xhr.addEventListener('abort', function transferCanceled(event) {

			if (error) {
				return;
			}

			error = new Error('Transfer aborted');
			error.status = error.number = 0;

			done();
		}, false);

		if (this.timeout) {
			timeout = this.timeout;
		} else {
			timeout = Math.max(Blast.state.rtt_timeout + 500, this.max_timeout);
		}

		// Create a timeout checker
		bomb = Blast.Bound.Function.timebomb(timeout, function onTimeout() {

			if (error || finished) {
				return;
			}

			if (!Blast.state.connectionHasTimedOut(timeout)) {
				temp_timeout = Blast.state.msUntilTimeout(timeout);

				if (!temp_timeout || temp_timeout < 500) {
					temp_timeout = 500;
				}

				console.log('Timeout of', timeout, 'is not yet considered as a connection timeout, waiting another', temp_timeout, 'ms');
				bomb = Blast.Bound.Function.timebomb(temp_timeout, onTimeout);
				return;
			}

			error = new Error('Transfer timeout after ' + timeout + 'ms for ' + that.url);
			error.status = error.number = 408;
			error.timeout = timeout;
			error.request_start = xhr[START_SYMBOL];

			Blast.state.reportError(error);

			try {
				xhr.abort();
			} catch (err) {
				// Ignore
			}

			done();
		});

		// Event fired when we start UPLOADING data
		// This is actually not that useful in retrospect
		//xhr.addEventListener('loadstart', function uploadingData(event) {});

		// We are receiving progress update
		xhr.addEventListener('progress', function receivingProgress(event) {

			if (xhr.readyState < 2) {
				return;
			}

			Blast.state.reportSuccess('progress', event);
		});

		// Listen for ready state changes
		xhr.addEventListener('readystatechange', function onStateChange(event) {

			if (xhr.readyState < 2 || xhr.readyState === 4) {
				return;
			}

			Blast.state.reportSuccess('readystatechange', event);
		});

		// Listen for successful load event
		xhr.addEventListener('load', function transferComplete(event) {

			var download_url,
			    disposition = xhr.getResponseHeader('content-disposition'),
			    filename,
			    reader,
			    anchor;

			response = xhr.response || xhr.responseText;
			that.response = response;
			type = xhr.getResponseHeader('content-type') || '';

			if (type && type.indexOf(';') > -1) {
				type = Blast.Bound.String.before(type, ';');
			}

			// Intercept file downloads
			if (disposition && disposition.search('attachment') !== -1) {

				// Just browse to it if filereader doesn't exist
				if (typeof FileReader == 'undefined') {
					window.location = href;
					return done();
				}

				// Try getting the filename if it's available
				filename = /filename="(.*?)"/.exec(disposition);

				if (filename && filename[1]) {
					filename = filename[1];
				} else {
					filename = that.url.href.split('/').pop() || 'download';
				}

				// We don't need to do the anchor trick on IE
				// (It won't work either, access denied)
				if (navigator.msSaveOrOpenBlob != null) {
					return navigator.msSaveOrOpenBlob(response, filename);
				}

				// Create a blob url
				download_url = URL.createObjectURL(response);

				// Create a temporary anchor for manipulating the filename
				anchor = document.createElement('a');
				document.body.appendChild(anchor);
				anchor.style = 'display: none';
				anchor.href = download_url;

				// Set the filename
				anchor.download = filename;

				// Download the file
				anchor.click();

				window.URL.revokeObjectURL(download_url);
				return done();
			}

			if (typeof FileReader == 'undefined') {
				result = response;
				return done();
			}

			reader = new FileReader();

			reader.onloadend = function onReaderLoadend() {
				result = reader.result;

				if (!type) {
					type = response.type;
				} else if (type != response.type) {
					// Sometimes (on Chrome at least)
					// the type is set to "text/xml", even when we're sending
					// a JSON string. Seems like onloadend is fired too early?
					// Or the data in "response" hasn't been updated yet, at least
				}

				done();
			};

			reader.readAsText(xhr.response);
		}, false);

		// Open the request
		xhr.open(method.name, this.url.href);

		// Set a request if needed
		if (this.timeout != null) {
			xhr.timeout = this.timeout;
		}

		// Always get the response as a blob
		xhr.responseType = 'blob';

		// Set the ajax header
		xhr.setRequestHeader('x-requested-with', 'XMLHttpRequest');

		for (key in this.headers) {
			xhr.setRequestHeader(key, this.headers[key]);
		}

		if (method.has_body && body) {

			if (body.constructor && body.constructor.name == 'FormData') {
				is_form = true;
			} else if (typeof FormData != 'undefined' && body instanceof FormData) {
				is_form = true;
			}

			if (typeof body == 'object' && !is_form) {
				body = JSON.stringify(body);
				xhr.setRequestHeader('content-type', 'application/json');
			}

			this.serialized_body = body;

			xhr.send(body);
		} else {
			xhr.send();
		}

		// Function that'll cleanup the request
		// & resolve or reject the pledge
		function done() {

			// If it already finished, do nothing
			if (finished) {
				return;
			}

			finished = true;
			that.time_ended = Date.now();

			if (!error && xhr.status > 399) {
				error = new Error(xhr.statusText);
				error.status = error.number = xhr.status;
			}

			if (type && type.indexOf('json') > -1 && result) {
				try {
					result = Collection.JSON.undry(result);
				} catch (err) {
					console.error('Error parsing JSON string:', result, err);
					return pledge.reject(err);
				}
			}

			if (error) {
				error.result = result;
				pledge.reject(error);
				that.error = error;
			} else {
				that.result = result;
				pledge.resolve(result);
				Blast.state.reportSuccess();
			}
		}

		// Check the connection state
		Blast.state.checkConnection().done(function onCheck(err, result) {

			// If an error already fired, do nothing
			if (error) {
				return;
			}

			if (err) {
				error = err;
			} else if (!result) {
				error = new Error('Connection failed');
			}

			if (error) {
				done();
			}
		});

		return pledge;
	});

	/**
	 * Get a response header
	 *
	 * @author   Jelle De Loecker   <jelle@develry.be>
	 * @since    0.7.0
	 * @version  0.7.0
	 */
	Request.setMethod(function getResponseHeader(name) {
		return this.xhr.getResponseHeader(name);
	});

	/**
	 * Hook into the original open method
	 *
	 * @author   Jelle De Loecker   <jelle@develry.be>
	 * @since    0.6.2
	 * @version  0.7.0
	 *
	 * @param    {String}   method
	 * @param    {String}   url
	 */
	XMLHttpRequest.prototype.open = function open(method, url) {
		this.open_method = method;
		this.open_url = url;
		this[START_SYMBOL] = Date.now();
		Blast.emit('xhr_open', this);
		return original_open.apply(this, arguments);
	};

	/**
	 * Hook into the original send method
	 *
	 * @author   Jelle De Loecker   <jelle@develry.be>
	 * @since    0.6.2
	 * @version  0.7.0
	 */
	XMLHttpRequest.prototype.send = function send() {
		this[START_SYMBOL] = Date.now();
		Blast.emit('xhr_send', this);
		return original_send.apply(this, arguments);
	};
};<commit_msg>Add missing variable declaration to request_browser<commit_after>module.exports = function BlastRequestBrowser(Blast, Collection) {

	"use strict";

	var Request = Blast.Classes.Develry.Request,
	    original_send = XMLHttpRequest.prototype.send,
	    original_open = XMLHttpRequest.prototype.open,
	    START_SYMBOL = Symbol('start');

	/**
	 * Actually make the request
	 *
	 * @author   Jelle De Loecker   <jelle@develry.be>
	 * @since    0.6.2
	 * @version  0.7.0
	 *
	 * @return   {Pledge}
	 */
	Request.setMethod(function _make_request() {

		var that = this,
		    pledge = new Pledge(),
		    method = this.method_info,
		    temp_timeout,
		    response,
		    finished,
		    is_form,
		    timeout,
		    result,
		    error,
		    body = this.body,
		    bomb,
		    type,
		    key,
		    xhr;

		if (this.get) {
			this.url.addQuery(this.get);
		}

		if (this.cache === false) {
			this.url.param('_', this.time_started);
		} else {
			this.url.param('_', null);
		}

		// Create the request
		xhr = new XMLHttpRequest();
		this.xhr = xhr;

		// Allow cancelling the request
		pledge.cancel = function cancel() {
			xhr.abort();
		};

		// DNS failures or no available connection will cause this error
		xhr.addEventListener('error', function onError(event) {

			error = new Error('Transfer failed');

			// Simulate a 408 "timeout"
			error.status = error.number = 408;

			Blast.state.reportError(error);

			done();
		}, false);

		// Catch aborts
		xhr.addEventListener('abort', function transferCanceled(event) {

			if (error) {
				return;
			}

			error = new Error('Transfer aborted');
			error.status = error.number = 0;

			done();
		}, false);

		if (this.timeout) {
			timeout = this.timeout;
		} else {
			timeout = Math.max(Blast.state.rtt_timeout + 500, this.max_timeout);
		}

		// Create a timeout checker
		bomb = Blast.Bound.Function.timebomb(timeout, function onTimeout() {

			if (error || finished) {
				return;
			}

			if (!Blast.state.connectionHasTimedOut(timeout)) {
				temp_timeout = Blast.state.msUntilTimeout(timeout);

				if (!temp_timeout || temp_timeout < 500) {
					temp_timeout = 500;
				}

				console.log('Timeout of', timeout, 'is not yet considered as a connection timeout, waiting another', temp_timeout, 'ms');
				bomb = Blast.Bound.Function.timebomb(temp_timeout, onTimeout);
				return;
			}

			error = new Error('Transfer timeout after ' + timeout + 'ms for ' + that.url);
			error.status = error.number = 408;
			error.timeout = timeout;
			error.request_start = xhr[START_SYMBOL];

			Blast.state.reportError(error);

			try {
				xhr.abort();
			} catch (err) {
				// Ignore
			}

			done();
		});

		// Event fired when we start UPLOADING data
		// This is actually not that useful in retrospect
		//xhr.addEventListener('loadstart', function uploadingData(event) {});

		// We are receiving progress update
		xhr.addEventListener('progress', function receivingProgress(event) {

			if (xhr.readyState < 2) {
				return;
			}

			Blast.state.reportSuccess('progress', event);
		});

		// Listen for ready state changes
		xhr.addEventListener('readystatechange', function onStateChange(event) {

			if (xhr.readyState < 2 || xhr.readyState === 4) {
				return;
			}

			Blast.state.reportSuccess('readystatechange', event);
		});

		// Listen for successful load event
		xhr.addEventListener('load', function transferComplete(event) {

			var download_url,
			    disposition = xhr.getResponseHeader('content-disposition'),
			    filename,
			    reader,
			    anchor;

			response = xhr.response || xhr.responseText;
			that.response = response;
			type = xhr.getResponseHeader('content-type') || '';

			if (type && type.indexOf(';') > -1) {
				type = Blast.Bound.String.before(type, ';');
			}

			// Intercept file downloads
			if (disposition && disposition.search('attachment') !== -1) {

				// Just browse to it if filereader doesn't exist
				if (typeof FileReader == 'undefined') {
					window.location = href;
					return done();
				}

				// Try getting the filename if it's available
				filename = /filename="(.*?)"/.exec(disposition);

				if (filename && filename[1]) {
					filename = filename[1];
				} else {
					filename = that.url.href.split('/').pop() || 'download';
				}

				// We don't need to do the anchor trick on IE
				// (It won't work either, access denied)
				if (navigator.msSaveOrOpenBlob != null) {
					return navigator.msSaveOrOpenBlob(response, filename);
				}

				// Create a blob url
				download_url = URL.createObjectURL(response);

				// Create a temporary anchor for manipulating the filename
				anchor = document.createElement('a');
				document.body.appendChild(anchor);
				anchor.style = 'display: none';
				anchor.href = download_url;

				// Set the filename
				anchor.download = filename;

				// Download the file
				anchor.click();

				window.URL.revokeObjectURL(download_url);
				return done();
			}

			if (typeof FileReader == 'undefined') {
				result = response;
				return done();
			}

			reader = new FileReader();

			reader.onloadend = function onReaderLoadend() {
				result = reader.result;

				if (!type) {
					type = response.type;
				} else if (type != response.type) {
					// Sometimes (on Chrome at least)
					// the type is set to "text/xml", even when we're sending
					// a JSON string. Seems like onloadend is fired too early?
					// Or the data in "response" hasn't been updated yet, at least
				}

				done();
			};

			reader.readAsText(xhr.response);
		}, false);

		// Open the request
		xhr.open(method.name, this.url.href);

		// Set a request if needed
		if (this.timeout != null) {
			xhr.timeout = this.timeout;
		}

		// Always get the response as a blob
		xhr.responseType = 'blob';

		// Set the ajax header
		xhr.setRequestHeader('x-requested-with', 'XMLHttpRequest');

		for (key in this.headers) {
			xhr.setRequestHeader(key, this.headers[key]);
		}

		if (method.has_body && body) {

			if (body.constructor && body.constructor.name == 'FormData') {
				is_form = true;
			} else if (typeof FormData != 'undefined' && body instanceof FormData) {
				is_form = true;
			}

			if (typeof body == 'object' && !is_form) {
				body = JSON.stringify(body);
				xhr.setRequestHeader('content-type', 'application/json');
			}

			this.serialized_body = body;

			xhr.send(body);
		} else {
			xhr.send();
		}

		// Function that'll cleanup the request
		// & resolve or reject the pledge
		function done() {

			// If it already finished, do nothing
			if (finished) {
				return;
			}

			finished = true;
			that.time_ended = Date.now();

			if (!error && xhr.status > 399) {
				error = new Error(xhr.statusText);
				error.status = error.number = xhr.status;
			}

			if (type && type.indexOf('json') > -1 && result) {
				try {
					result = Collection.JSON.undry(result);
				} catch (err) {
					console.error('Error parsing JSON string from "' + that.url.path + '":', result, err);
					return pledge.reject(err);
				}
			}

			if (error) {
				error.result = result;
				pledge.reject(error);
				that.error = error;
			} else {
				that.result = result;
				pledge.resolve(result);
				Blast.state.reportSuccess();
			}
		}

		// Check the connection state
		Blast.state.checkConnection().done(function onCheck(err, result) {

			// If an error already fired, do nothing
			if (error) {
				return;
			}

			if (err) {
				error = err;
			} else if (!result) {
				error = new Error('Connection failed');
			}

			if (error) {
				done();
			}
		});

		return pledge;
	});

	/**
	 * Get a response header
	 *
	 * @author   Jelle De Loecker   <jelle@develry.be>
	 * @since    0.7.0
	 * @version  0.7.0
	 */
	Request.setMethod(function getResponseHeader(name) {
		return this.xhr.getResponseHeader(name);
	});

	/**
	 * Hook into the original open method
	 *
	 * @author   Jelle De Loecker   <jelle@develry.be>
	 * @since    0.6.2
	 * @version  0.7.0
	 *
	 * @param    {String}   method
	 * @param    {String}   url
	 */
	XMLHttpRequest.prototype.open = function open(method, url) {
		this.open_method = method;
		this.open_url = url;
		this[START_SYMBOL] = Date.now();
		Blast.emit('xhr_open', this);
		return original_open.apply(this, arguments);
	};

	/**
	 * Hook into the original send method
	 *
	 * @author   Jelle De Loecker   <jelle@develry.be>
	 * @since    0.6.2
	 * @version  0.7.0
	 */
	XMLHttpRequest.prototype.send = function send() {
		this[START_SYMBOL] = Date.now();
		Blast.emit('xhr_send', this);
		return original_send.apply(this, arguments);
	};
};<|endoftext|>


<commit_before>var crypto = require('crypto'),
    path = require('path'),
    fs = require('fs');

var uuid = require('node-uuid');

var db = require('../db.js'),
    util = require('../util'),
    defaults = require('../defaults.js'),
    settings = require('../settings');

function create_update(req, res, next) {
  var body = req.body;
  settings.set(body);
  settings.save(function(err) {
    if(err) return next(err);
    res.json(201, settings);
  });
}
exports.create = exports.update = util.loginRequired(create_update);

function show(req, res) {
  res.json(settings.toJSON());
}
exports.show = util.loginRequired(show);

function connectionTest(req, res, next) {
  var body = req.body;

  db.connect(body.uri, function(err) {
    if(err) return next(err);
    res.json(200, {ok: true});
  });
}
exports.connectionTest = connectionTest;

function tableCreation(req, res, next) {
  var body = req.body;
  res.set('Content-Type','text/event-stream');

  db.syncDB({
    uri: body.uri,

    oncomplete: function(err) {
      if(err) return next(err);
      res.end();
    },

    onprogress: function(script) {
      res.write('data: ' + JSON.stringify({script: script}) + '\n\n');
    },

    forceDrop: false
  });
}
exports.tableCreation = tableCreation;

function commit(req, res, next) {
  var body = req. body;

  settings.set({
    DB: body.uri,
    SITE_ID: uuid.v4(),
    SECRET_KEY: crypto.randomBytes(64).toString('base64')
  });

  settings.save(settings.DEFAULT_FILENAME, function(err) {
    if(err) return next(err);
    res.json(201, {ok: true});
  });
}
exports.commit = commit;

function createUser(req, res, next) {
  var body = req.body;

  db.users.create(body, function(err, user) {
    console.log(arguments)
    if(err) return next(err);
    res.json(201, user);
  });
}
exports.createUser = createUser;<commit_msg>Removed console.log<commit_after>var crypto = require('crypto'),
    path = require('path'),
    fs = require('fs');

var uuid = require('node-uuid');

var db = require('../db.js'),
    util = require('../util'),
    defaults = require('../defaults.js'),
    settings = require('../settings');

function create_update(req, res, next) {
  var body = req.body;
  settings.set(body);
  settings.save(function(err) {
    if(err) return next(err);
    res.json(201, settings);
  });
}
exports.create = exports.update = util.loginRequired(create_update);

function show(req, res) {
  res.json(settings.toJSON());
}
exports.show = util.loginRequired(show);

function connectionTest(req, res, next) {
  var body = req.body;

  db.connect(body.uri, function(err) {
    if(err) return next(err);
    res.json(200, {ok: true});
  });
}
exports.connectionTest = connectionTest;

function tableCreation(req, res, next) {
  var body = req.body;
  res.set('Content-Type','text/event-stream');

  db.syncDB({
    uri: body.uri,

    oncomplete: function(err) {
      if(err) return next(err);
      res.end();
    },

    onprogress: function(script) {
      res.write('data: ' + JSON.stringify({script: script}) + '\n\n');
    },

    forceDrop: false
  });
}
exports.tableCreation = tableCreation;

function commit(req, res, next) {
  var body = req. body;

  settings.set({
    DB: body.uri,
    SITE_ID: uuid.v4(),
    SECRET_KEY: crypto.randomBytes(64).toString('base64')
  });

  settings.save(settings.DEFAULT_FILENAME, function(err) {
    if(err) return next(err);
    res.json(201, {ok: true});
  });
}
exports.commit = commit;

function createUser(req, res, next) {
  var body = req.body;

  db.users.create(body, function(err, user) {
    if(err) return next(err);
    res.json(201, user);
  });
}
exports.createUser = createUser;<|endoftext|>


<commit_before>import {
  isArray,
  isMaybeThenable
} from './utils';

import {
  noop,
  resolve,
  handleMaybeThenable,
  reject,
  fulfill,
  subscribe,
  FULFILLED,
  REJECTED,
  PENDING,
  getThen
} from './-internal';

import Promise from './promise';
import originalThen from './then';
import originalResolve from './promise/resolve';

export function makeSettledResult(state, position, value) {
  if (state === FULFILLED) {
    return {
      state: 'fulfilled',
      value: value
    };
  } else {
     return {
      state: 'rejected',
      reason: value
    };
  }
}

function Enumerator(Constructor, input, abortOnReject, label) {
  var enumerator = this;

  enumerator._instanceConstructor = Constructor;
  enumerator.promise = new Constructor(noop, label);
  enumerator._abortOnReject = abortOnReject;

  if (enumerator._validateInput(input)) {
    enumerator._input     = input;
    enumerator.length     = input.length;
    enumerator._remaining = input.length;

    enumerator._init();

    if (enumerator.length === 0) {
      fulfill(enumerator.promise, enumerator._result);
    } else {
      enumerator.length = enumerator.length || 0;
      enumerator._enumerate();
      if (enumerator._remaining === 0) {
        fulfill(enumerator.promise, enumerator._result);
      }
    }
  } else {
    reject(enumerator.promise, enumerator._validationError());
  }
}

export default Enumerator;

Enumerator.prototype._validateInput = function(input) {
  return isArray(input);
};

Enumerator.prototype._validationError = function() {
  return new Error('Array Methods must be provided an Array');
};

Enumerator.prototype._init = function() {
  this._result = new Array(this.length);
};

Enumerator.prototype._enumerate = function() {
  var enumerator = this;
  var length     = enumerator.length;
  var promise    = enumerator.promise;
  var input      = enumerator._input;

  for (var i = 0; promise._state === PENDING && i < length; i++) {
    enumerator._eachEntry(input[i], i);
  }
};

Enumerator.prototype._eachEntry = function(entry, i) {
  var enumerator = this;
  var c = enumerator._instanceConstructor;
  var resolve = c.resolve;
  if (isMaybeThenable(entry)) {
    if (resolve === originalResolve) {
      var then = getThen(entry);

      if (then === originalThen &&
          entry._state !== PENDING) {
          entry._onError = null;
          enumerator._settledAt(entry._state, i, entry._result);
      } else if (typeof then !== 'function') {
        enumerator._result[i] = enumerator._makeResult(FULFILLED, i, entry);
      } else if (c === Promise) {
        var promise = new c(noop);
        handleMaybeThenable(promise, entry, then);
        enumerator._willSettleAt(promise, i);
      } else {
        enumerator._willSettleAt(resolve(entry), i);
      }
    } else {
      enumerator._willSettleAt(resolve(entry), i);
    }
  } else {
    enumerator._remaining--;
    enumerator._result[i] = enumerator._makeResult(FULFILLED, i, entry);
  }
};

Enumerator.prototype._settledAt = function(state, i, value) {
  var enumerator = this;
  var promise = enumerator.promise;

  if (promise._state === PENDING) {
    enumerator._remaining--;

    if (enumerator._abortOnReject && state === REJECTED) {
      reject(promise, value);
    } else {
      enumerator._result[i] = enumerator._makeResult(state, i, value);
    }
  }

  if (enumerator._remaining === 0) {
    fulfill(promise, enumerator._result);
  }
};

Enumerator.prototype._makeResult = function(state, i, value) {
  return value;
};

Enumerator.prototype._willSettleAt = function(promise, i) {
  var enumerator = this;

  subscribe(promise, undefined, function(value) {
    enumerator._settledAt(FULFILLED, i, value);
  }, function(reason) {
    enumerator._settledAt(REJECTED, i, reason);
  });
};
<commit_msg>leave not<commit_after>import {
  isArray,
  isMaybeThenable
} from './utils';

import {
  noop,
  resolve,
  handleMaybeThenable,
  reject,
  fulfill,
  subscribe,
  FULFILLED,
  REJECTED,
  PENDING,
  getThen
} from './-internal';

import Promise from './promise';
import originalThen from './then';
import originalResolve from './promise/resolve';

export function makeSettledResult(state, position, value) {
  if (state === FULFILLED) {
    return {
      state: 'fulfilled',
      value: value
    };
  } else {
     return {
      state: 'rejected',
      reason: value
    };
  }
}

function Enumerator(Constructor, input, abortOnReject, label) {
  var enumerator = this;

  enumerator._instanceConstructor = Constructor;
  enumerator.promise = new Constructor(noop, label);
  enumerator._abortOnReject = abortOnReject;

  if (enumerator._validateInput(input)) {
    enumerator._input     = input;
    enumerator.length     = input.length;
    enumerator._remaining = input.length;

    enumerator._init();

    if (enumerator.length === 0) {
      fulfill(enumerator.promise, enumerator._result);
    } else {
      enumerator.length = enumerator.length || 0;
      enumerator._enumerate();
      if (enumerator._remaining === 0) {
        fulfill(enumerator.promise, enumerator._result);
      }
    }
  } else {
    reject(enumerator.promise, enumerator._validationError());
  }
}

export default Enumerator;

Enumerator.prototype._validateInput = function(input) {
  return isArray(input);
};

Enumerator.prototype._validationError = function() {
  return new Error('Array Methods must be provided an Array');
};

Enumerator.prototype._init = function() {
  this._result = new Array(this.length);
};

Enumerator.prototype._enumerate = function() {
  var enumerator = this;
  var length     = enumerator.length;
  var promise    = enumerator.promise;
  var input      = enumerator._input;

  for (var i = 0; promise._state === PENDING && i < length; i++) {
    enumerator._eachEntry(input[i], i);
  }
};

Enumerator.prototype._eachEntry = function(entry, i) {
  var enumerator = this;
  var c = enumerator._instanceConstructor;
  var resolve = c.resolve;
  if (isMaybeThenable(entry)) {
    if (resolve === originalResolve) {
      var then = getThen(entry);

      if (then === originalThen &&
          entry._state !== PENDING) {
          entry._onError = null;
          enumerator._settledAt(entry._state, i, entry._result);
      } else if (typeof then !== 'function') {
        enumerator._result[i] = enumerator._makeResult(FULFILLED, i, entry);
      } else if (c === Promise) {
        var promise = new c(noop);
        handleMaybeThenable(promise, entry, then);
        enumerator._willSettleAt(promise, i);
      } else {
        // this path invokes `then` twice
        enumerator._willSettleAt(resolve(entry), i);
      }
    } else {
      enumerator._willSettleAt(resolve(entry), i);
    }
  } else {
    enumerator._remaining--;
    enumerator._result[i] = enumerator._makeResult(FULFILLED, i, entry);
  }
};

Enumerator.prototype._settledAt = function(state, i, value) {
  var enumerator = this;
  var promise = enumerator.promise;

  if (promise._state === PENDING) {
    enumerator._remaining--;

    if (enumerator._abortOnReject && state === REJECTED) {
      reject(promise, value);
    } else {
      enumerator._result[i] = enumerator._makeResult(state, i, value);
    }
  }

  if (enumerator._remaining === 0) {
    fulfill(promise, enumerator._result);
  }
};

Enumerator.prototype._makeResult = function(state, i, value) {
  return value;
};

Enumerator.prototype._willSettleAt = function(promise, i) {
  var enumerator = this;

  subscribe(promise, undefined, function(value) {
    enumerator._settledAt(FULFILLED, i, value);
  }, function(reason) {
    enumerator._settledAt(REJECTED, i, reason);
  });
};
<|endoftext|>


<commit_before>var ramp = require("ramp");
var Url = require("url");
var colorizer = require("ansi-colorizer");
var remoteRunner = require("./browser/remote-runner");
var reporters = require("buster-test").reporters;
var stackFilter = require("stack-filter");
var _ = require("lodash");

// Error codes, as per FreeBSD's sysexit(3)
// Errors are mapped to sysexit(3) error codes wherever that makes sense
var EX_DATAERR = 65;
var EX_SOFTWARE = 70;
var EX_TEMPFAIL = 75;
var EX_PROTOCOL = 76;
var EX_CONFIG = 78;

function serverURL(options) {
    var href = options && options.server || "";
    if (href && !/:\/\//.test(href)) { href = "http://" + href; }
    return Url.parse(href);
}

function createRampClient(options) {
    var url = serverURL(options);
    return ramp.createRampClient(url.port, url.hostname);
}

function terminalOptions(logger, options) {
    return {
        outputStream: logger.streamForLevel("log"),
        color: !!options.color,
        bright: !!options.bright
    };
}

function listenForUncaughtExceptions(sess, logger, S) {
    var listeners = sess.listeners && sess.listeners.uncaughtException || [];
    if (listeners.length > 0) { return; }
    sess.on("uncaughtException", function (msg) {
        logger.warn(S.yellow("Uncaught exception: " + msg.data.message));
    });
}

function opt(options, key, defaultValue) {
    return options.hasOwnProperty(key) ? options[key] : defaultValue;
}

function runnerOptions(options) {
    return {
        failOnNoAssertions: opt(options, "failOnNoAssertions", true),
        autoRun: opt(options, "autoRun", true),
        captureConsole: opt(options, "captureConsole", true),
        filters: opt(options, "filters", undefined)
    };
}

function noSlavesError(options) {
    var server = serverURL(options);
    server = server && server.href || "http://??";
    var error = new Error("No slaves connected, nothing to do.\n" +
                          "Capture browsers at " + server + " and try again.");
    error.code = EX_PROTOCOL;
    error.type = "NoSlavesError";
    return error;
}

function noSlaves(session, options, callback) {
    session.end();
    callback(noSlavesError(options));
}

function loadReporter(session, terminalOpt, options) {
    var server = serverURL(options);
    var host = server.hostname + ":" + server.port;

    var reporter = reporters.load(options.reporter || "brief").create(
        _.extend({
            logPassedMessages: !!options.logPassedMessages,
            displayProgress: false,
            verbosity: options.verbosity,
            stackFilter: stackFilter.configure({
                cwd: "http://" + host + session.getSession().resourcesPath,
                filters: !options.filterStacks ? [] : ["./buster/"]
            })
        }, terminalOpt)
    );
    reporter.contextsInPackageName = 2;
    return reporter;
}

function sessionError(options, callback, error) {
    if (/ECONNREFUSED/.test(error.message)) {
        var server = serverURL(options);
        error = new Error(
            "Unable to connect to server\n" +
                "Please make sure that buster-server is running at " +
                server.href
        );
        error.code = EX_TEMPFAIL;
    } else {
        error.message = "Failed creating session: " + error.message;
    }

    callback(error);
}

function extractFile(error) {
    var match = error.match(/'(.*)'/);
    if (!match) { return ""; }
    var cwd = process.cwd() + "/";
    return match[1].replace(cwd, "");
}

function configError(callback, err) {
    var error = err;

    if (/ENOENT/.test(err.message) && /'.*\*.*'/.test(err.message)) {
        error = new Error("Configured pattern '" + extractFile(err.message) +
                          "' does not match any files");
        error.code = EX_DATAERR;
    } else if (/ENOENT/.test(err.message)) {
        error = new Error("Configured path '" + extractFile(err.message) +
                          "' is not a file or directory");
        error.code = EX_DATAERR;
    }
    callback(error);
}

function cacheability(cacheable, resourceSet) {
    if (!cacheable) {
        resourceSet.forEach(function (resource) {
            resource.cacheable = false;
        });
    }
    return resourceSet;
}

var testRun = {
    create: function (config, options, logger, done) {
        var run = Object.create(this);
        run.config = config;
        run.options = options;
        run.callback = done;
        run.logger = logger;
        run.cacheable = options.cacheable;
        run.formatter = colorizer.configure(terminalOptions(logger, options));
        return run;
    },

    done: function (err) {
        if (!this.callback) { return; }
        if (err) { err.code = err.code || EX_SOFTWARE; }
        this.callback.apply(this, arguments);
        delete this.callback;
    },

    abort: function (err) {
        this.aborted = true;
        this.done(err);
    },

    endSession: function (session) {
        this.logger.info("Successfully closed session");
        session.endSession();
    },

    extensionHook: function () {
        try {
            this.config.runExtensionHook.apply(this.config, arguments);
        } catch (err) {
            err.code = EX_SOFTWARE;
            return err;
        }
    },

    createRemoteRunner: function (session) {
        return remoteRunner.create(session, this.logger);
    },

    attachReporter: function (runner, session) {
        var terminalOpt = terminalOptions(this.logger, this.options);
        loadReporter(session, terminalOpt, _.extend({
            verbosity: this.logger.level
        }, this.options)).listen(runner);
    },

    runTests: function (session, callback) {
        listenForUncaughtExceptions(session, this.logger, this.formatter);
        var runner = this.createRemoteRunner(session);
        this.attachReporter(runner, session);

        var hookError = this.extensionHook("beforeRun");

        if (hookError) {
            this.endSession(session);
            return callback(hookError);
        }

        runner.on("exception", function (err) {
            this.endSession(session);
            callback(err);
        }.bind(this));

        session.initialize().then(
            function (sessionClient) {
                var slaves = sessionClient.getInitialSlaves();

                if (slaves.length === 0) {
                    return noSlaves(sessionClient, this.options, callback);
                }

                runner.setSlaves(slaves);
                var hookError = this.extensionHook("testRun", runner, sessionClient);

                if (hookError) {
                    this.endSession(sessionClient);
                    return callback(hookError);
                }

                runner.on("suite:end", function (results) {
                    this.endSession(sessionClient);
                    callback(null, results);
                }.bind(this));
            }.bind(this),
            function (error) {
                this.endSession(session);
                return callback(error);
            }.bind(this));
    },

    startSession: function (client, callback) {
        return function (resourceSet) {
            if (this.aborted) { return callback(); }
            this.logger.info("Creating browser session");

            resourceSet.addResource({
                path: "/buster/configure-test-run.js",
                content: "buster.configureTestRun(" + JSON.stringify({
                    failOnNoAssertions: opt(this.options, "failOnNoAssertions", true),
                    autoRun: opt(this.options, "autoRun", true),
                    captureConsole: opt(this.options, "captureConsole", true),
                    filters: opt(this.options, "filters", undefined)
                }) + ");"
            }).then(function () {
                resourceSet.loadPath.append("/buster/configure-test-run.js");

                client.createSession(cacheability(this.cacheable, resourceSet), {
                    staticResourcesPath: !!this.options.staticResourcesPath
                }).then(
                    callback.bind(null, null),
                    sessionError.bind(null, this.options, callback)
                );
            }.bind(this));
        }.bind(this);
    },

    start: function () {
        var client = createRampClient(this.options);
        var self = this;
        var done = function () { return self.done.apply(self, arguments); };
        this.config.resolve().then(this.startSession(client, function (err, sessionInit) {
            if (err) { return done(err); }
            this.onInterrupt(function () { sessionInit.endSession(); });

            sessionInit.onSessionAbort(function (e) {
                done({ message: e.error });
            }.bind(this));

            try {
                if (this.aborted) { return this.endSession(sessionInit); }
                this.options = _.extend({}, this.options, this.config.options);
                this.runTests(sessionInit, done);
            } catch (e) {
                done(e);
            }
        }.bind(this)), configError.bind(null, done));
    },

    onInterrupt: function (callback) {
        process.on("SIGINT", function () {
            callback();
            process.exit(1);
        });
    }
};

module.exports = {
    testRun: testRun,

    run: function (config, options, done) {
        var run = testRun.create(config, options || {}, this.logger, done);
        run.start();
        return run;
    }
};
<commit_msg>Temporarily avoid error-like object with Buffer<commit_after>var ramp = require("ramp");
var Url = require("url");
var colorizer = require("ansi-colorizer");
var remoteRunner = require("./browser/remote-runner");
var reporters = require("buster-test").reporters;
var stackFilter = require("stack-filter");
var _ = require("lodash");

// Error codes, as per FreeBSD's sysexit(3)
// Errors are mapped to sysexit(3) error codes wherever that makes sense
var EX_DATAERR = 65;
var EX_SOFTWARE = 70;
var EX_TEMPFAIL = 75;
var EX_PROTOCOL = 76;
var EX_CONFIG = 78;

function serverURL(options) {
    var href = options && options.server || "";
    if (href && !/:\/\//.test(href)) { href = "http://" + href; }
    return Url.parse(href);
}

function createRampClient(options) {
    var url = serverURL(options);
    return ramp.createRampClient(url.port, url.hostname);
}

function terminalOptions(logger, options) {
    return {
        outputStream: logger.streamForLevel("log"),
        color: !!options.color,
        bright: !!options.bright
    };
}

function listenForUncaughtExceptions(sess, logger, S) {
    var listeners = sess.listeners && sess.listeners.uncaughtException || [];
    if (listeners.length > 0) { return; }
    sess.on("uncaughtException", function (msg) {
        logger.warn(S.yellow("Uncaught exception: " + msg.data.message));
    });
}

function opt(options, key, defaultValue) {
    return options.hasOwnProperty(key) ? options[key] : defaultValue;
}

function runnerOptions(options) {
    return {
        failOnNoAssertions: opt(options, "failOnNoAssertions", true),
        autoRun: opt(options, "autoRun", true),
        captureConsole: opt(options, "captureConsole", true),
        filters: opt(options, "filters", undefined)
    };
}

function noSlavesError(options) {
    var server = serverURL(options);
    server = server && server.href || "http://??";
    var error = new Error("No slaves connected, nothing to do.\n" +
                          "Capture browsers at " + server + " and try again.");
    error.code = EX_PROTOCOL;
    error.type = "NoSlavesError";
    return error;
}

function noSlaves(session, options, callback) {
    session.end();
    callback(noSlavesError(options));
}

function loadReporter(session, terminalOpt, options) {
    var server = serverURL(options);
    var host = server.hostname + ":" + server.port;

    var reporter = reporters.load(options.reporter || "brief").create(
        _.extend({
            logPassedMessages: !!options.logPassedMessages,
            displayProgress: false,
            verbosity: options.verbosity,
            stackFilter: stackFilter.configure({
                cwd: "http://" + host + session.getSession().resourcesPath,
                filters: !options.filterStacks ? [] : ["./buster/"]
            })
        }, terminalOpt)
    );
    reporter.contextsInPackageName = 2;
    return reporter;
}

function sessionError(options, callback, error) {
    if (/ECONNREFUSED/.test(error.message)) {
        var server = serverURL(options);
        error = new Error(
            "Unable to connect to server\n" +
                "Please make sure that buster-server is running at " +
                server.href
        );
        error.code = EX_TEMPFAIL;
    } else {
        error.message = "Failed creating session: " + error.message;
    }

    callback(error);
}

function extractFile(error) {
    var match = error.match(/'(.*)'/);
    if (!match) { return ""; }
    var cwd = process.cwd() + "/";
    return match[1].replace(cwd, "");
}

function configError(callback, err) {
    var error = err;

    if (/ENOENT/.test(err.message) && /'.*\*.*'/.test(err.message)) {
        error = new Error("Configured pattern '" + extractFile(err.message) +
                          "' does not match any files");
        error.code = EX_DATAERR;
    } else if (/ENOENT/.test(err.message)) {
        error = new Error("Configured path '" + extractFile(err.message) +
                          "' is not a file or directory");
        error.code = EX_DATAERR;
    }
    callback(error);
}

function cacheability(cacheable, resourceSet) {
    if (!cacheable) {
        resourceSet.forEach(function (resource) {
            resource.cacheable = false;
        });
    }
    return resourceSet;
}

var testRun = {
    create: function (config, options, logger, done) {
        var run = Object.create(this);
        run.config = config;
        run.options = options;
        run.callback = done;
        run.logger = logger;
        run.cacheable = options.cacheable;
        run.formatter = colorizer.configure(terminalOptions(logger, options));
        return run;
    },

    done: function (err) {
        if (!this.callback) { return; }
        if (err) { err.code = err.code || EX_SOFTWARE; }
        this.callback.apply(this, arguments);
        delete this.callback;
    },

    abort: function (err) {
        this.aborted = true;
        this.done(err);
    },

    endSession: function (session) {
        this.logger.info("Successfully closed session");
        session.endSession();
    },

    extensionHook: function () {
        try {
            this.config.runExtensionHook.apply(this.config, arguments);
        } catch (err) {
            err.code = EX_SOFTWARE;
            return err;
        }
    },

    createRemoteRunner: function (session) {
        return remoteRunner.create(session, this.logger);
    },

    attachReporter: function (runner, session) {
        var terminalOpt = terminalOptions(this.logger, this.options);
        loadReporter(session, terminalOpt, _.extend({
            verbosity: this.logger.level
        }, this.options)).listen(runner);
    },

    runTests: function (session, callback) {
        listenForUncaughtExceptions(session, this.logger, this.formatter);
        var runner = this.createRemoteRunner(session);
        this.attachReporter(runner, session);

        var hookError = this.extensionHook("beforeRun");

        if (hookError) {
            this.endSession(session);
            return callback(hookError);
        }

        runner.on("exception", function (err) {
            this.endSession(session);
            callback(err);
        }.bind(this));

        session.initialize().then(
            function (sessionClient) {
                var slaves = sessionClient.getInitialSlaves();

                if (slaves.length === 0) {
                    return noSlaves(sessionClient, this.options, callback);
                }

                runner.setSlaves(slaves);
                var hookError = this.extensionHook("testRun", runner, sessionClient);

                if (hookError) {
                    this.endSession(sessionClient);
                    return callback(hookError);
                }

                runner.on("suite:end", function (results) {
                    this.endSession(sessionClient);
                    callback(null, results);
                }.bind(this));
            }.bind(this),
            function (error) {
                this.endSession(session);
                // Temporary, ramp provides a Buffer for the message
                error.message = error.message.toString();
                return callback(error);
            }.bind(this));
    },

    startSession: function (client, callback) {
        return function (resourceSet) {
            if (this.aborted) { return callback(); }
            this.logger.info("Creating browser session");

            resourceSet.addResource({
                path: "/buster/configure-test-run.js",
                content: "buster.configureTestRun(" + JSON.stringify({
                    failOnNoAssertions: opt(this.options, "failOnNoAssertions", true),
                    autoRun: opt(this.options, "autoRun", true),
                    captureConsole: opt(this.options, "captureConsole", true),
                    filters: opt(this.options, "filters", undefined)
                }) + ");"
            }).then(function () {
                resourceSet.loadPath.append("/buster/configure-test-run.js");

                client.createSession(cacheability(this.cacheable, resourceSet), {
                    staticResourcesPath: !!this.options.staticResourcesPath
                }).then(
                    callback.bind(null, null),
                    sessionError.bind(null, this.options, callback)
                );
            }.bind(this));
        }.bind(this);
    },

    start: function () {
        var client = createRampClient(this.options);
        var self = this;
        var done = function () { return self.done.apply(self, arguments); };
        this.config.resolve().then(this.startSession(client, function (err, sessionInit) {
            if (err) { return done(err); }
            this.onInterrupt(function () { sessionInit.endSession(); });

            sessionInit.onSessionAbort(function (e) {
                done({ message: e.error });
            }.bind(this));

            try {
                if (this.aborted) { return this.endSession(sessionInit); }
                this.options = _.extend({}, this.options, this.config.options);
                this.runTests(sessionInit, done);
            } catch (e) {
                done(e);
            }
        }.bind(this)), configError.bind(null, done));
    },

    onInterrupt: function (callback) {
        process.on("SIGINT", function () {
            callback();
            process.exit(1);
        });
    }
};

module.exports = {
    testRun: testRun,

    run: function (config, options, done) {
        var run = testRun.create(config, options || {}, this.logger, done);
        run.start();
        return run;
    }
};
<|endoftext|>


<commit_before>global._init = (function() {
    const game = require('@screeps/engine/src/game/game');
    const fakeConsole = require('@screeps/engine/src/game/console');
    const WorldMapGrid = require('./mapgrid');
    const utils = require('@screeps/engine/src/utils');
    const _ = require('lodash');

    const isolate = _isolate;
    const context = _context;
    const ivm = _ivm;
    const cpuHalt = _halt;
    const customObjectPrototypes = _customObjectPrototypes;
    const customIntentTypes = _customIntentTypes
    let mapGrid, staticTerrainData = {}, scope;

    function nowCpuTime() {
        return isolate.cpuTime[0] * 1e3 + isolate.cpuTime[1] / 1e6;
    }

    module.exports.isolate = isolate;
    module.exports.context = context;

    _.each(customObjectPrototypes, cop => {
        _.each(cop.opts.properties, (fn, key) => {
            cop.opts.properties[key] = eval(`(${fn})`)
        })
        if (cop.opts.prototypeExtender) {
            if(cop.opts.prototypeExtender.startsWith('prototypeExtender')) {
              cop.opts.prototypeExtender = `function ${cop.opts.prototypeExtender}`
            }
            cop.opts.prototypeExtender = eval(`(${cop.opts.prototypeExtender})`)
        }
    })
    global._setStaticTerrainData = function (buffer, roomOffsets) {
        for (let room in roomOffsets) {
            staticTerrainData[room] = new Uint8Array(buffer, roomOffsets[room], 2500);
        }
    };

    global._evalFn = function(fnString) {
        eval('('+fnString+')(scope)');
    };

    global._start = function (data) {

        let activeSegments, publicSegments, defaultPublicSegment, activeForeignSegment;
        let startTime, startDirtyTime = nowCpuTime();
        let intentCpu = 0.2,
            freeMethods = {say: true, pull: true};

        let intents = {
            list: {},
            cpu: 0,
            set(id, name, data) {
                this.list[id] = this.list[id] || {};
                if (!freeMethods[name] && !this.list[id][name]) {
                    this.cpu += intentCpu;
                }
                this.list[id][name] = data;
            },
            push(name, data, maxLen) {
                this.list[name] = this.list[name] || [];
                if (maxLen && this.list[name].length >= maxLen) {
                    return false;
                }
                this.list[name].push(data);
                this.cpu += intentCpu;
                return true;
            },
            pushByName(id, name, data, maxLen) {
                this.list[id] = this.list[id] || {};
                this.list[id][name] = this.list[id][name] || [];
                if (maxLen && this.list[id][name].length >= maxLen) {
                    return false;
                }
                this.list[id][name].push(data);
                this.cpu += intentCpu;
                return true;
            },
            remove(id, name) {
                if (this.list[id] && this.list[id][name]) {
                    delete this.list[id][name];
                    return true;
                }
                return false;
            }
        };

        var rawMemory = Object.create(null, {
            get: {
                value: function () {
                    return data.userMemory.data;
                }
            },
            set: {
                value: function (value) {
                    if (!_.isString(value)) {
                        throw new Error('Raw memory value is not a string');
                    }
                    if (value.length > 2 * 1024 * 1024) {
                        throw new Error('Raw memory length exceeded 2 MB limit');
                    }
                    if (this._parsed) {
                        delete this._parsed;
                    }
                    data.userMemory.data = value;
                }
            },
            segments: {
                value: Object.create(null)
            },
            interShardSegment: {
                value: '',
                writable: true
            },
            setActiveSegments: {
                value: function (ids) {
                    if (!_.isArray(ids)) {
                        throw new Error(`"${ids}" is not an array`);
                    }
                    if (ids.length > 10) {
                        throw new Error('Only 10 memory segments can be active at the same time');
                    }
                    activeSegments = [];
                    for (var i = 0; i < ids.length; i++) {
                        var id = parseInt(ids[i]);
                        if (_.isNaN(id) || id > 99 || id < 0) {
                            throw new Error(`"${ids[i]}" is not a valid segment ID`);
                        }
                        activeSegments.push(id);
                    }

                }
            },
            setPublicSegments: {
                value: function (ids) {
                    if (!_.isArray(ids)) {
                        throw new Error(`"${ids}" is not an array`);
                    }
                    publicSegments = [];
                    for (var i = 0; i < ids.length; i++) {
                        var id = parseInt(ids[i]);
                        if (_.isNaN(id) || id > 99 || id < 0) {
                            throw new Error(`"${ids[i]}" is not a valid segment ID`);
                        }
                        publicSegments.push(id);
                    }
                }
            },
            setDefaultPublicSegment: {
                value: function (id) {
                    if (id !== null) {
                        id = parseInt(id);
                        if (_.isNaN(id) || id > 99 || id < 0) {
                            throw new Error(`"${id}" is not a valid segment ID`);
                        }
                    }
                    defaultPublicSegment = id;
                }
            },
            setActiveForeignSegment: {
                value: function (username, id) {
                    if (username === null) {
                        activeForeignSegment = null;
                        return;
                    }
                    if (id !== undefined) {
                        id = parseInt(id);
                        if (_.isNaN(id) || id > 99 || id < 0) {
                            throw new Error(`"${id}" is not a valid segment ID`);
                        }
                    }
                    activeForeignSegment = {username, id};
                }
            }
        });

        if (data.memorySegments) {
            for (var i in data.memorySegments) {
                rawMemory.segments[i] = data.memorySegments[i];
            }
        }

        if (data.foreignMemorySegment) {
            rawMemory.foreignSegment = Object.create(null);
            Object.assign(rawMemory.foreignSegment, data.foreignMemorySegment);
        }

        var getUsedCpu = function () {
            return nowCpuTime() + intents.cpu - startTime;
        };

        if(!mapGrid) {
            mapGrid = new WorldMapGrid(JSON.parse(data.accessibleRooms), staticTerrainData);
        }

        data.mapGrid = mapGrid;
        data.staticTerrainData = staticTerrainData;
        data.customObjectPrototypes = customObjectPrototypes;

        var outMessage = {};

        scope = game.init(
            global,
            data.userCode,
            data,
            intents,
            rawMemory,
            fakeConsole.makeConsole(data.user._id, function(fn) { return fn }),
            data.consoleCommands,
            data.cpu,
            getUsedCpu,
            function (name) {},
            undefined,
            function() {
                return isolate.getHeapStatisticsSync();
            },
            cpuHalt);

        return new ivm.Reference(function() {

            startTime = nowCpuTime();

            try {
                game.run(data.user._id);
                outMessage.type = 'done';
            }
            catch (e) {
                outMessage.type = 'error';
                outMessage.error = _.isObject(e) && e.stack || e.toString();
            }

            if (rawMemory._parsed) {
                data.userMemory.data = JSON.stringify(rawMemory._parsed);
            }

            var segmentKeys = Object.keys(rawMemory.segments);
            if (segmentKeys.length > 0) {
                if (segmentKeys.length > 10) {
                    throw 'Cannot save more than 10 memory segments on the same tick';
                }
                outMessage.memorySegments = {};
                for (var i = 0; i < segmentKeys.length; i++) {
                    var key = parseInt(segmentKeys[i]);
                    if (_.isNaN(key) || key < 0 || key > 99) {
                        throw `"${segmentKeys[i]}" is not a valid memory segment ID`;
                    }
                    if (typeof rawMemory.segments[segmentKeys[i]] != 'string') {
                        throw `Memory segment #${segmentKeys[i]} is not a string`;
                    }
                    if (rawMemory.segments[segmentKeys[i]].length > 100 * 1024) {
                        throw `Memory segment #${segmentKeys[i]} has exceeded 100 KB length limit`;
                    }
                    outMessage.memorySegments[key] = "" + rawMemory.segments[segmentKeys[i]];
                }
            }

            outMessage.usedTime = Math.ceil(nowCpuTime() - startTime + intents.cpu);
            outMessage.usedDirtyTime = Math.ceil(nowCpuTime() - startDirtyTime);
            outMessage.intents = utils.storeIntents(data.user._id, intents.list, data, customIntentTypes);
            outMessage.intentsCpu = intents.cpu;
            outMessage.memory = data.userMemory;
            outMessage.console = {
                log: fakeConsole.getMessages(data.user._id),
                results: fakeConsole.getCommandResults(data.user._id)
            };

            let visual = fakeConsole.getVisual(data.user._id);
            if (Object.keys(visual).length > 0) {
                outMessage.visual = visual;
            }

            outMessage.activeSegments = activeSegments;
            outMessage.activeForeignSegment = activeForeignSegment;
            outMessage.defaultPublicSegment = defaultPublicSegment;
            if (publicSegments) {
                outMessage.publicSegments = publicSegments.join(',');
            }

            data = null;  // Important - preventing memory leak

            return new ivm.ExternalCopy(outMessage).copyInto();
        });
    }
});
<commit_msg>🎨 Minor code style fix<commit_after>global._init = (function() {
    const game = require('@screeps/engine/src/game/game');
    const fakeConsole = require('@screeps/engine/src/game/console');
    const WorldMapGrid = require('./mapgrid');
    const utils = require('@screeps/engine/src/utils');
    const _ = require('lodash');

    const isolate = _isolate;
    const context = _context;
    const ivm = _ivm;
    const cpuHalt = _halt;
    const customObjectPrototypes = _customObjectPrototypes;
    const customIntentTypes = _customIntentTypes;
    let mapGrid, staticTerrainData = {}, scope;

    function nowCpuTime() {
        return isolate.cpuTime[0] * 1e3 + isolate.cpuTime[1] / 1e6;
    }

    module.exports.isolate = isolate;
    module.exports.context = context;

    _.each(customObjectPrototypes, cop => {
        _.each(cop.opts.properties, (fn, key) => {
            cop.opts.properties[key] = eval(`(${fn})`)
        })
        if (cop.opts.prototypeExtender) {
            if(cop.opts.prototypeExtender.startsWith('prototypeExtender')) {
              cop.opts.prototypeExtender = `function ${cop.opts.prototypeExtender}`
            }
            cop.opts.prototypeExtender = eval(`(${cop.opts.prototypeExtender})`)
        }
    })
    global._setStaticTerrainData = function (buffer, roomOffsets) {
        for (let room in roomOffsets) {
            staticTerrainData[room] = new Uint8Array(buffer, roomOffsets[room], 2500);
        }
    };

    global._evalFn = function(fnString) {
        eval('('+fnString+')(scope)');
    };

    global._start = function (data) {

        let activeSegments, publicSegments, defaultPublicSegment, activeForeignSegment;
        let startTime, startDirtyTime = nowCpuTime();
        let intentCpu = 0.2,
            freeMethods = {say: true, pull: true};

        let intents = {
            list: {},
            cpu: 0,
            set(id, name, data) {
                this.list[id] = this.list[id] || {};
                if (!freeMethods[name] && !this.list[id][name]) {
                    this.cpu += intentCpu;
                }
                this.list[id][name] = data;
            },
            push(name, data, maxLen) {
                this.list[name] = this.list[name] || [];
                if (maxLen && this.list[name].length >= maxLen) {
                    return false;
                }
                this.list[name].push(data);
                this.cpu += intentCpu;
                return true;
            },
            pushByName(id, name, data, maxLen) {
                this.list[id] = this.list[id] || {};
                this.list[id][name] = this.list[id][name] || [];
                if (maxLen && this.list[id][name].length >= maxLen) {
                    return false;
                }
                this.list[id][name].push(data);
                this.cpu += intentCpu;
                return true;
            },
            remove(id, name) {
                if (this.list[id] && this.list[id][name]) {
                    delete this.list[id][name];
                    return true;
                }
                return false;
            }
        };

        var rawMemory = Object.create(null, {
            get: {
                value: function () {
                    return data.userMemory.data;
                }
            },
            set: {
                value: function (value) {
                    if (!_.isString(value)) {
                        throw new Error('Raw memory value is not a string');
                    }
                    if (value.length > 2 * 1024 * 1024) {
                        throw new Error('Raw memory length exceeded 2 MB limit');
                    }
                    if (this._parsed) {
                        delete this._parsed;
                    }
                    data.userMemory.data = value;
                }
            },
            segments: {
                value: Object.create(null)
            },
            interShardSegment: {
                value: '',
                writable: true
            },
            setActiveSegments: {
                value: function (ids) {
                    if (!_.isArray(ids)) {
                        throw new Error(`"${ids}" is not an array`);
                    }
                    if (ids.length > 10) {
                        throw new Error('Only 10 memory segments can be active at the same time');
                    }
                    activeSegments = [];
                    for (var i = 0; i < ids.length; i++) {
                        var id = parseInt(ids[i]);
                        if (_.isNaN(id) || id > 99 || id < 0) {
                            throw new Error(`"${ids[i]}" is not a valid segment ID`);
                        }
                        activeSegments.push(id);
                    }

                }
            },
            setPublicSegments: {
                value: function (ids) {
                    if (!_.isArray(ids)) {
                        throw new Error(`"${ids}" is not an array`);
                    }
                    publicSegments = [];
                    for (var i = 0; i < ids.length; i++) {
                        var id = parseInt(ids[i]);
                        if (_.isNaN(id) || id > 99 || id < 0) {
                            throw new Error(`"${ids[i]}" is not a valid segment ID`);
                        }
                        publicSegments.push(id);
                    }
                }
            },
            setDefaultPublicSegment: {
                value: function (id) {
                    if (id !== null) {
                        id = parseInt(id);
                        if (_.isNaN(id) || id > 99 || id < 0) {
                            throw new Error(`"${id}" is not a valid segment ID`);
                        }
                    }
                    defaultPublicSegment = id;
                }
            },
            setActiveForeignSegment: {
                value: function (username, id) {
                    if (username === null) {
                        activeForeignSegment = null;
                        return;
                    }
                    if (id !== undefined) {
                        id = parseInt(id);
                        if (_.isNaN(id) || id > 99 || id < 0) {
                            throw new Error(`"${id}" is not a valid segment ID`);
                        }
                    }
                    activeForeignSegment = {username, id};
                }
            }
        });

        if (data.memorySegments) {
            for (var i in data.memorySegments) {
                rawMemory.segments[i] = data.memorySegments[i];
            }
        }

        if (data.foreignMemorySegment) {
            rawMemory.foreignSegment = Object.create(null);
            Object.assign(rawMemory.foreignSegment, data.foreignMemorySegment);
        }

        var getUsedCpu = function () {
            return nowCpuTime() + intents.cpu - startTime;
        };

        if(!mapGrid) {
            mapGrid = new WorldMapGrid(JSON.parse(data.accessibleRooms), staticTerrainData);
        }

        data.mapGrid = mapGrid;
        data.staticTerrainData = staticTerrainData;
        data.customObjectPrototypes = customObjectPrototypes;

        var outMessage = {};

        scope = game.init(
            global,
            data.userCode,
            data,
            intents,
            rawMemory,
            fakeConsole.makeConsole(data.user._id, function(fn) { return fn }),
            data.consoleCommands,
            data.cpu,
            getUsedCpu,
            function (name) {},
            undefined,
            function() {
                return isolate.getHeapStatisticsSync();
            },
            cpuHalt);

        return new ivm.Reference(function() {

            startTime = nowCpuTime();

            try {
                game.run(data.user._id);
                outMessage.type = 'done';
            }
            catch (e) {
                outMessage.type = 'error';
                outMessage.error = _.isObject(e) && e.stack || e.toString();
            }

            if (rawMemory._parsed) {
                data.userMemory.data = JSON.stringify(rawMemory._parsed);
            }

            var segmentKeys = Object.keys(rawMemory.segments);
            if (segmentKeys.length > 0) {
                if (segmentKeys.length > 10) {
                    throw 'Cannot save more than 10 memory segments on the same tick';
                }
                outMessage.memorySegments = {};
                for (var i = 0; i < segmentKeys.length; i++) {
                    var key = parseInt(segmentKeys[i]);
                    if (_.isNaN(key) || key < 0 || key > 99) {
                        throw `"${segmentKeys[i]}" is not a valid memory segment ID`;
                    }
                    if (typeof rawMemory.segments[segmentKeys[i]] != 'string') {
                        throw `Memory segment #${segmentKeys[i]} is not a string`;
                    }
                    if (rawMemory.segments[segmentKeys[i]].length > 100 * 1024) {
                        throw `Memory segment #${segmentKeys[i]} has exceeded 100 KB length limit`;
                    }
                    outMessage.memorySegments[key] = "" + rawMemory.segments[segmentKeys[i]];
                }
            }

            outMessage.usedTime = Math.ceil(nowCpuTime() - startTime + intents.cpu);
            outMessage.usedDirtyTime = Math.ceil(nowCpuTime() - startDirtyTime);
            outMessage.intents = utils.storeIntents(data.user._id, intents.list, data, customIntentTypes);
            outMessage.intentsCpu = intents.cpu;
            outMessage.memory = data.userMemory;
            outMessage.console = {
                log: fakeConsole.getMessages(data.user._id),
                results: fakeConsole.getCommandResults(data.user._id)
            };

            let visual = fakeConsole.getVisual(data.user._id);
            if (Object.keys(visual).length > 0) {
                outMessage.visual = visual;
            }

            outMessage.activeSegments = activeSegments;
            outMessage.activeForeignSegment = activeForeignSegment;
            outMessage.defaultPublicSegment = defaultPublicSegment;
            if (publicSegments) {
                outMessage.publicSegments = publicSegments.join(',');
            }

            data = null;  // Important - preventing memory leak

            return new ivm.ExternalCopy(outMessage).copyInto();
        });
    }
});
<|endoftext|>


<commit_before>var Scope = require('uniscope'),
    _ = require('lodash'),
    postmanLegacyInterface = require('./postman-legacy-interface'),
    PostmanConsole = require('./console'),

    scopeLibraries = {
        JSON: require('liquid-json'),
        Buffer: require('buffer'),
        _: require('lodash3').noConflict(),
        CryptoJS: require('crypto-js'),
        atob: require('atob'),
        btoa: require('btoa'),
        tv4: require('tv4'),
        xml2Json: require('./xml2Json'),
        Backbone: require('backbone'),
        cheerio: require('cheerio')
    };

module.exports = {
    listener: function (prefix, console) {
        var scope = Scope.create({
            eval: true,
            ignore: ['require'],
            block: ['bridge']
        });

        /**
         * @param {String} id
         * @param {String} code
         * @param {Object} options
         * @param {Object} options.masked
         * @param {Object} options.globals
         */
        return function (id, code, options) {
            !options && (options = {});

            var bridge = this,
                cursor = options.masked && options.masked.cursor || {},
                dispatchEventName = prefix + id,
                done = (function (dispatchEventName) {
                    var sealed = false;

                    return function (err) {
                        if (sealed) { return; }
                        sealed = true;
                        bridge.dispatch(dispatchEventName, err, options);

                    };
                }(dispatchEventName));

            scope.import(scopeLibraries);
            scope.import(options.globals);
            scope.import({
                postman: postmanLegacyInterface.create(options),
                console: new PostmanConsole(bridge, cursor, options.debug ? console : null)
            });

            scope.exec(code, done);
        };
    }
};
<commit_msg>Lint fixes!<commit_after>var Scope = require('uniscope'),
    postmanLegacyInterface = require('./postman-legacy-interface'),
    PostmanConsole = require('./console'),

    scopeLibraries = {
        JSON: require('liquid-json'),
        Buffer: require('buffer'),
        _: require('lodash3').noConflict(),
        CryptoJS: require('crypto-js'),
        atob: require('atob'),
        btoa: require('btoa'),
        tv4: require('tv4'),
        xml2Json: require('./xml2Json'),
        Backbone: require('backbone'),
        cheerio: require('cheerio')
    };

module.exports = {
    listener: function (prefix, console) {
        var scope = Scope.create({
            eval: true,
            ignore: ['require'],
            block: ['bridge']
        });

        /**
         * @param {String} id
         * @param {String} code
         * @param {Object} options
         * @param {Object} options.masked
         * @param {Object} options.globals
         */
        return function (id, code, options) {
            !options && (options = {});

            var bridge = this,
                cursor = options.masked && options.masked.cursor || {},
                dispatchEventName = prefix + id,
                done = (function (dispatchEventName) {
                    var sealed = false;

                    return function (err) {
                        if (sealed) { return; }
                        sealed = true;
                        bridge.dispatch(dispatchEventName, err, options);

                    };
                }(dispatchEventName));

            scope.import(scopeLibraries);
            scope.import(options.globals);
            scope.import({
                postman: postmanLegacyInterface.create(options),
                console: new PostmanConsole(bridge, cursor, options.debug ? console : null)
            });

            scope.exec(code, done);
        };
    }
};
<|endoftext|>


<commit_before>var Utils          = require("./utils")
  , Mixin          = require("./associations/mixin")
  , QueryGenerator = require("./query-generator")

var Model = module.exports = function(values, options) {
  var self = this

  this.definition = null // will be set in Model.build
  this.attributes = []
  this.options = options || {}

  // add all passed values to the model and store the attribute names in this.attributes
  Utils._.map(values, function(value, key) { self.addAttribute(key, value) })

  // set id to null if not passed as value
  // a newly created model has no id
  var defaults = this.options.hasPrimaryKeys ? {} : { id: null }

  if(this.options.timestamps) {
    defaults[this.options.underscored ? 'created_at' : 'createdAt'] = new Date()
    defaults[this.options.underscored ? 'updated_at' : 'updatedAt'] = new Date()

    if(this.options.paranoid)
      defaults[this.options.underscored ? 'deleted_at' : 'deletedAt'] = null
  }

  Utils._.map(defaults, function(value, attr) {
    if(!self.hasOwnProperty(attr))
      self.addAttribute(attr, value)
  })
}
Utils.addEventEmitter(Model)
Utils._.map(Mixin.classMethods, function(fct, name) { Model[name] = fct })

Model.Events = {
  insert: 'InsertQuery',
  update: 'UpdateQuery',
  destroy: 'DestroyQuery'
}

Model.prototype.addAttribute = function(attribute, value) {
  this[attribute] = value
  this.attributes.push(attribute)
}

Model.prototype.query = function() {
  var args = Utils._.map(arguments, function(arg, _) { return arg })
    , s    = this.definition.modelManager.sequelize

  args.push(this)
  return s.query.apply(s, args)
}

Model.prototype.save = function() {
  var attr = this.options.underscored ? 'updated_at' : 'updatedAt'

  if(this.hasOwnProperty(attr))
    this[attr] = new Date()

  if(this.isNewRecord) {
    var self = this
    var eventEmitter = new Utils.CustomEventEmitter(function() {
      self.query(QueryGenerator.insertQuery(self.definition.tableName, self.values))
      .on('success', function(obj) {
        obj.isNewRecord = false
        eventEmitter.emit('success', obj)
      })
      .on('failure', function(err) { eventEmitter.emit('failure', err) })
    })
    return eventEmitter.run()
  } else {
    var identifier = this.options.hasPrimaryKeys ? this.primaryKeyValues : this.id
    return this.query(QueryGenerator.updateQuery(this.definition.tableName, this.values, identifier))
  }
}

Model.prototype.updateAttributes = function(updates) {
  var self = this

  var readOnlyAttributes = Utils._.keys(this.definition.primaryKeys)
  readOnlyAttributes.push('id')
  readOnlyAttributes.push('createdAt')
  readOnlyAttributes.push('updatedAt')
  readOnlyAttributes.push('deletedAt')

  Utils._.each(updates, function(value, attr) {
    var updateAllowed = (
      (readOnlyAttributes.indexOf(attr) == -1) &&
      (readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) &&
      (self.attributes.indexOf(attr) > -1)
    )
    updateAllowed && (self[attr] = value)
  })
  return this.save()
}

Model.prototype.destroy = function() {
  if(this.options.timestamps && this.options.paranoid) {
    this[this.options.underscored ? 'deleted_at' : 'deletedAt'] = new Date()
    return this.save()
  } else {
    var identifier = this.options.hasPrimaryKeys ? this.primaryKeyValues : this.id
    return this.query(QueryGenerator.deleteQuery(this.definition.tableName, identifier))
  }
}

Model.prototype.__defineGetter__("identifiers", function() {
  var primaryKeys = Utils._.keys(this.definition.primaryKeys)
    , result      = {}
    , self        = this

  if(!this.definition.hasPrimaryKeys)
    primaryKeys = ['id']

  primaryKeys.forEach(function(identifier) {
    result[identifier] = self[identifier]
  })

  return result
})

Model.prototype.__defineGetter__('isDeleted', function() {
  var result = this.options.timestamps && this.options.paranoid
  result = result && this[this.options.underscored ? 'deleted_at' : 'deletedAt'] != null

  return result
})

Model.prototype.__defineGetter__('values', function() {
  var result = {}
    , self   = this

  this.attributes.forEach(function(attr) {
    result[attr] = self[attr]
  })

  return result
})

Model.prototype.__defineGetter__('primaryKeyValues', function() {
  var result = {}
    , self   = this

  Utils._.each(this.definition.primaryKeys, function(_, attr) {
    result[attr] = self[attr]
  })

  return result
})

Model.prototype.equals = function(other) {
  var result = true
    , self   = this

  Utils._.each(this.values, function(value, key) {
    result = result && (value == other[key])
  })

  return result
}

Model.prototype.equalsOneOf = function(others) {
  var result = false
    , self   = this

  others.forEach(function(other) { result = result || self.equals(other) })

  return result
}

/* Add the instance methods to Model */
Utils._.map(Mixin.instanceMethods, function(fct, name) { Model.prototype[name] = fct})<commit_msg>better "scoping" for options attribute<commit_after>var Utils          = require("./utils")
  , Mixin          = require("./associations/mixin")
  , QueryGenerator = require("./query-generator")

var Model = module.exports = function(values, options) {
  var self = this

  this.definition = null // will be set in Model.build
  this.attributes = []
  this.__options = options || {}

  // add all passed values to the model and store the attribute names in this.attributes
  Utils._.map(values, function(value, key) { self.addAttribute(key, value) })

  // set id to null if not passed as value
  // a newly created model has no id
  var defaults = this.__options.hasPrimaryKeys ? {} : { id: null }

  if(this.__options.timestamps) {
    defaults[this.__options.underscored ? 'created_at' : 'createdAt'] = new Date()
    defaults[this.__options.underscored ? 'updated_at' : 'updatedAt'] = new Date()

    if(this.__options.paranoid)
      defaults[this.__options.underscored ? 'deleted_at' : 'deletedAt'] = null
  }

  Utils._.map(defaults, function(value, attr) {
    if(!self.hasOwnProperty(attr))
      self.addAttribute(attr, value)
  })
}
Utils.addEventEmitter(Model)
Utils._.map(Mixin.classMethods, function(fct, name) { Model[name] = fct })

Model.Events = {
  insert: 'InsertQuery',
  update: 'UpdateQuery',
  destroy: 'DestroyQuery'
}

Model.prototype.addAttribute = function(attribute, value) {
  this[attribute] = value
  this.attributes.push(attribute)
}

Model.prototype.query = function() {
  var args = Utils._.map(arguments, function(arg, _) { return arg })
    , s    = this.definition.modelManager.sequelize

  args.push(this)
  return s.query.apply(s, args)
}

Model.prototype.save = function() {
  var attr = this.__options.underscored ? 'updated_at' : 'updatedAt'

  if(this.hasOwnProperty(attr))
    this[attr] = new Date()

  if(this.isNewRecord) {
    var self = this
    var eventEmitter = new Utils.CustomEventEmitter(function() {
      self.query(QueryGenerator.insertQuery(self.definition.tableName, self.values))
      .on('success', function(obj) {
        obj.isNewRecord = false
        eventEmitter.emit('success', obj)
      })
      .on('failure', function(err) { eventEmitter.emit('failure', err) })
    })
    return eventEmitter.run()
  } else {
    var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id
    return this.query(QueryGenerator.updateQuery(this.definition.tableName, this.values, identifier))
  }
}

Model.prototype.updateAttributes = function(updates) {
  var self = this

  var readOnlyAttributes = Utils._.keys(this.definition.primaryKeys)
  readOnlyAttributes.push('id')
  readOnlyAttributes.push('createdAt')
  readOnlyAttributes.push('updatedAt')
  readOnlyAttributes.push('deletedAt')

  Utils._.each(updates, function(value, attr) {
    var updateAllowed = (
      (readOnlyAttributes.indexOf(attr) == -1) &&
      (readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) &&
      (self.attributes.indexOf(attr) > -1)
    )
    updateAllowed && (self[attr] = value)
  })
  return this.save()
}

Model.prototype.destroy = function() {
  if(this.__options.timestamps && this.__options.paranoid) {
    this[this.__options.underscored ? 'deleted_at' : 'deletedAt'] = new Date()
    return this.save()
  } else {
    var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id
    return this.query(QueryGenerator.deleteQuery(this.definition.tableName, identifier))
  }
}

Model.prototype.__defineGetter__("identifiers", function() {
  var primaryKeys = Utils._.keys(this.definition.primaryKeys)
    , result      = {}
    , self        = this

  if(!this.definition.hasPrimaryKeys)
    primaryKeys = ['id']

  primaryKeys.forEach(function(identifier) {
    result[identifier] = self[identifier]
  })

  return result
})

Model.prototype.__defineGetter__('isDeleted', function() {
  var result = this.__options.timestamps && this.__options.paranoid
  result = result && this[this.__options.underscored ? 'deleted_at' : 'deletedAt'] != null

  return result
})

Model.prototype.__defineGetter__('values', function() {
  var result = {}
    , self   = this

  this.attributes.forEach(function(attr) {
    result[attr] = self[attr]
  })

  return result
})

Model.prototype.__defineGetter__('primaryKeyValues', function() {
  var result = {}
    , self   = this

  Utils._.each(this.definition.primaryKeys, function(_, attr) {
    result[attr] = self[attr]
  })

  return result
})

Model.prototype.equals = function(other) {
  var result = true
    , self   = this

  Utils._.each(this.values, function(value, key) {
    result = result && (value == other[key])
  })

  return result
}

Model.prototype.equalsOneOf = function(others) {
  var result = false
    , self   = this

  others.forEach(function(other) { result = result || self.equals(other) })

  return result
}

/* Add the instance methods to Model */
Utils._.map(Mixin.instanceMethods, function(fct, name) { Model.prototype[name] = fct})
<|endoftext|>