<commit_before>'use strict';

exports.name = 'nue';
exports.version = '0.5.0';
exports.flow = flow;
exports.parallel = parallel;
exports.as = as;

var util = require('util');
var assert = require('assert');
var debugMode = process.env.NODE_DEBUG && /\bnue\b/.test(process.env.NODE_DEBUG);
var flowId = 0;

function flow() {
  if (arguments.length === 1 && typeof arguments[0] === 'string') {
    var flowName = arguments[0];
    return function () {
      return  deferFlow(flowName, normalizeArgs(arguments));
    };
  } else {
    return deferFlow('', normalizeArgs(arguments));
  }

  function deferFlow(flowName, functions) {
    function startFlow() {
      var flow = {
        flowName: flowName,
        flowId: flowId++,
        context: this,
        isTopLevel: !(this instanceof StepContext),
        data: {},
        err: null
      };
      var steps = functions.length > 0 ? functions : [function () { this.next(); }];
      var head = chainSteps(flow, steps);
      head.apply(this, arguments);
    }
    startFlow.stepName = flowName;
    return startFlow;
  }

  function chainSteps(flow, steps) {
    var len = steps.length;
    var lastIndex = len - 1;
    var last = makeLastStep(steps[lastIndex], lastIndex);
    var isThrown = false;
    if (len === 1) {
      return last;
    }

    return steps.reduceRight(function chain(prev, curr, index) {
      assert.equal(typeof curr, 'function', 'Each argument for `flow` must be a function.');
      return function step() {
        var next = index === len - 2 ? last : prev;
        var context = new StepContext(flow, curr.stepName || curr.name, index, next, last);
        try {
          runStep(curr, context, arguments);
        } catch (e) {
          if (isThrown) {
            throw e;
          }
          StepContext.prototype.endWith.call(context, e);
        }
      };
    });

    function makeLastStep(fn, index) {
      assert.equal(typeof fn, 'function', 'Each argument for `flow` must be a function.');
      return function lastStep() {
        var context = new StepContext(flow, fn.stepName || fn.name, index, exit, exit);
        try {
          runStep(fn, context, arguments, true);
        } catch (e) {
          isThrown = true;
          throw e;
        }
      };

      function exit() {
        if (flow.isTopLevel) {
          if (flow.err) {
            var message = 'An error must be handled in a last step of its flow. ' +
              'To indicate error handling completed, set null to `this.err` before exiting the last step. ' +
              'cause: ' + util.inspect(flow.err);
            var err = new Error(message);
            err.name = 'NueUnhandledError';
            throw err;
          }
        } else {
          if (flow.err) {
            flow.context.endWith.call(flow.context, flow.err);
          } else {
            flow.context.next.apply(flow.context, arguments);
          }
        }
      }
    }

    function runStep(step, context, args) {
      if (debugMode) {
        debug('BEGIN flow: %s(%d), step: %s(%d), args:',
          context.flowName || '<anonymous>', context._flow.flowId,
          context.stepName || '<anonymous>', context.stepIndex, Array.prototype.slice.call(args));
      }
      try {
        step.apply(context, args);
      } catch (e) {
        if (debugMode) {
          debug('ERROR flow: %s(%d), step: %s(%d), err:',
            context.flowName || '<anonymous>', context._flow.flowId,
            context.stepName || '<anonymous>', context.stepIndex, e);
        }
        throw e;
      }
    }
  }
}

function parallel() {
  if (arguments.length === 1 && typeof arguments[0] === 'string') {
    var flowName = arguments[0];
    return function () {
      return deferParallel(flowName, normalizeArgs(arguments));
    };
  } else {
    return deferParallel('', normalizeArgs(arguments));
  }

  function deferParallel(flowName, functions) {
    function startParallel() {
      assert(this instanceof StepContext, 'A `parallel` must be inside a flow or another parallel.');
      var self = this;
      var args = Array.prototype.slice.call(arguments);
      self.asyncEach(1)(functions, function (fn, group) {
        assert.equal(typeof fn, 'function', 'Each argument for `parallel` must be a function.');
        var callback = group.async(As.ALL);
        var end = function end() {
          if (this.err) {
            self.endWith(this.err);
          } else {
            callback.apply(null, arguments);
          }
        };
        end.stepName = (fn.stepName || fn.name) + '_end';
        flow(flowName)(fn, end).apply(self, args);
      });
    }
    startParallel.stepName = flowName;
    return startParallel;
  }
}

function as(index) {
  return new As(index);
}

function normalizeArgs(args) {
  return (args.length === 1 && Array.isArray(args[0])) ? args[0] : Array.prototype.slice.call(args);
}

function getLocation(target) {
  var originalPrepareStackTrace = Error.prepareStackTrace;
  var originalStackTraceLimit = Error.stackTraceLimit;
  Error.prepareStackTrace = prepareStackTrace;
  Error.stackTraceLimit = 1;
  var err = {};
  Error.captureStackTrace(err, target);
  var stack = err.stack;
  Error.prepareStackTrace = originalPrepareStackTrace;
  Error.stackTraceLimit = originalStackTraceLimit;
  return {
    functionName: stack.getFunctionName(),
    fileName: stack.getFileName(),
    lineNumber: stack.getLineNumber(),
    columnNumber: stack.getColumnNumber()
  };

  function prepareStackTrace() {
    return arguments[1][0];
  }
}

function debug() {
  var args = Array.prototype.slice.call(arguments);
  args[0] = 'NUE ' + args[0];
  console.error.apply(console, args);
}

function As(index) {
  this.index = index;
}

As.ALL = {description: 'all'};

function StepContext(flow, name, index, next, last) {
  this._flow = flow;
  this._next = next;
  this._last = last;
  this._async = new Async(false, this.next.bind(this), this.endWith.bind(this));
  this.err = flow.err;
  this.data = flow.data;
  this.flowName = flow.flowName;
  this.stepName = name;
  this.stepIndex = index;
}

StepContext.DEFAULT_CONCURRENCY = 10;

StepContext.noop = function noop() {};

StepContext.prototype._disable = function _disable() {
  this.next = StepContext.noop;
  this.end = StepContext.noop;
  this.endWith = StepContext.noop;
};

StepContext.prototype.next = function next() {
  this._disable();
  this._flow.err = this.err;
  this._next.apply(null, arguments);
};

StepContext.prototype.end = function end() {
  this._disable();
  this._flow.err = this.err;
  this._last.apply(null, arguments);
};

StepContext.prototype.endWith = function endWith(err) {
  this._disable();
  this._flow.err = err;
  this._last.call(null);
};

StepContext.prototype.async = function async(mapping) {
  return this._async.makeCallback.call(this._async, mapping, async);
};

StepContext.prototype.asyncEach = function asyncEach() {
  var self = this;
  var callback = self.async(As.ALL);
  var asyncObj = new Async(true, callback.bind(self), self.endWith.bind(self));
  var group = {
    async: function async(mapping) {
      return asyncObj.makeCallback.call(asyncObj, mapping, async);
    }
  };

  if (arguments.length === 1 && typeof arguments[0] === 'number') {
    var concurrency = arguments[0];
    return function () {
      validateAndStart(concurrency, arguments[0], arguments[1]);
    };
  } else {
    validateAndStart(StepContext.DEFAULT_CONCURRENCY, arguments[0], arguments[1]);
  }

  function validateAndStart(concurrency, array, worker) {
    assert(Array.isArray(array), 'An argument `array` must be an array.');
    assert.equal(typeof worker, 'function', 'An argument `worker` must be a function.');
    process.nextTick(function startEach() {
      each(concurrency, array, worker, 0);
    });
  }

  function each(concurrency, array, worker, index) {
    var len = array.length;
    for (var i = 0; i < concurrency && index < len; i++, index++) {
      worker.call(self, array[index], group, index, array);
    }
    if (index === len) {
      process.nextTick(asyncObj.makeCallback(Async.SIGNAL_UNLOCK));
    } else {
      process.nextTick(function nextEach() {
        each(concurrency, array, worker, index);
      });
    }
  }
};

StepContext.prototype.exec = function exec(fn) {
  assert(arguments.length > 1, 'Arguments length must be more than 1.');
  assert.equal(typeof fn, 'function', 'The first argument must be a function.');
  assert.equal(typeof arguments[arguments.length - 1], 'function', 'The last argument must be a function.');
  var self = this;
  var callback = arguments[arguments.length - 1];
  var args = Array.prototype.slice.call(arguments, 1, arguments.length - 1);
  var end = function end() {
    callback.apply(null, [this.err].concat(Array.prototype.slice.call(arguments)));
  };
  end.stepName = (fn.stepName || fn.name) + '_end';
  process.nextTick(function execFlow() {
    flow('exec')(fn, end).apply(self, args);
  });
};

function Async(lock, next, endWith) {
  this.lock = lock;
  this.next = next;
  this.endWith = endWith;
  this.index = 0;
  this.pending = 0;
  this.isCanceled = false;
  this.results = [];
}

Async.SIGNAL_UNLOCK = {description: 'signal_unlock'};

Async.prototype.makeCallback = function makeCallback(mapping, caller) {
  assert(typeof mapping === 'object' && mapping !== null, 'An argument `mapping` must be an object');
  this.pending++;
  if (this.index === 0 && !this.lock) {
    this.lock = true;
    process.nextTick(this.makeCallback(Async.SIGNAL_UNLOCK));
  }
  var index = mapping === Async.SIGNAL_UNLOCK ? -1 : this.index++;
  var location = getLocation(caller);
  var self = this;

  return function asyncCallback(err) {
    self.pending--;
    if (!self.isCanceled) {
      if (err && isErrorHandleRequired(mapping)) {
        self.isCanceled = true;
        self.endWith.call(null, makeAsyncError(err));
      } else {
        if (mapping === Async.SIGNAL_UNLOCK) {
          self.lock = false;
        } else {
          self.results[index] = mapArguments(mapping, arguments);
        }
        if (self.pending === 0 && !self.lock) {
          self.next.apply(null, self.results);
        }
      }
    }
  };

  function isErrorHandleRequired(mapping) {
    if (mapping === Async.SIGNAL_UNLOCK || mapping === As.ALL) {
      return false;
    }
    if (mapping instanceof As) {
      return mapping.index !== 0;
    }
    return Object.keys(mapping).every(function (key) {
      var value = mapping[key];
      if (value instanceof As) {
        return value.index !== 0;
      }
      return true;
    });
  }

  function mapArguments(mapping, args) {
    if (mapping === As.ALL) {
      return Array.prototype.slice.call(args);
    }
    if (mapping instanceof As) {
      return args[mapping.index];
    }
    return Object.keys(mapping).reduce(function (result, key) {
      var value = mapping[key];
      if (value instanceof As) {
        result[key] = args[value.index];
      } else {
        result[key] = value;
      }
      return result;
    }, {});
  }

  function makeAsyncError(err) {
    var call = {
      'function': location.functionName,
      location: util.format('%s:%d:%d', location.fileName, location.lineNumber, location.columnNumber),
      mapping: mapping
    };
    var cause;
    var history;
    if (err.name === 'NueAsyncError') {
      cause = err.cause;
      history = err.asyncCallHistory.concat([call]);
    } else {
      cause = err;
      history = [call];
    }
    var e = new Error('An error occurred in an async call.');
    e.name = 'NueAsyncError';
    e.cause = cause;
    e.asyncCallHistory = history;
    e.message += util.format('\ncause stack is ...\n  %s\nasync call history is ...\n%s',
      e.cause.stack, util.inspect(history, false, null));
    return e;
  }
};
<commit_msg>refactor NueAsyncError message.<commit_after>'use strict';

exports.name = 'nue';
exports.version = '0.5.0';
exports.flow = flow;
exports.parallel = parallel;
exports.as = as;

var util = require('util');
var assert = require('assert');
var debugMode = process.env.NODE_DEBUG && /\bnue\b/.test(process.env.NODE_DEBUG);
var flowId = 0;

function flow() {
  if (arguments.length === 1 && typeof arguments[0] === 'string') {
    var flowName = arguments[0];
    return function () {
      return  deferFlow(flowName, normalizeArgs(arguments));
    };
  } else {
    return deferFlow('', normalizeArgs(arguments));
  }

  function deferFlow(flowName, functions) {
    function startFlow() {
      var flow = {
        flowName: flowName,
        flowId: flowId++,
        context: this,
        isTopLevel: !(this instanceof StepContext),
        data: {},
        err: null
      };
      var steps = functions.length > 0 ? functions : [function () { this.next(); }];
      var head = chainSteps(flow, steps);
      head.apply(this, arguments);
    }
    startFlow.stepName = flowName;
    return startFlow;
  }

  function chainSteps(flow, steps) {
    var len = steps.length;
    var lastIndex = len - 1;
    var last = makeLastStep(steps[lastIndex], lastIndex);
    var isThrown = false;
    if (len === 1) {
      return last;
    }

    return steps.reduceRight(function chain(prev, curr, index) {
      assert.equal(typeof curr, 'function', 'Each argument for `flow` must be a function.');
      return function step() {
        var next = index === len - 2 ? last : prev;
        var context = new StepContext(flow, curr.stepName || curr.name, index, next, last);
        try {
          runStep(curr, context, arguments);
        } catch (e) {
          if (isThrown) {
            throw e;
          }
          StepContext.prototype.endWith.call(context, e);
        }
      };
    });

    function makeLastStep(fn, index) {
      assert.equal(typeof fn, 'function', 'Each argument for `flow` must be a function.');
      return function lastStep() {
        var context = new StepContext(flow, fn.stepName || fn.name, index, exit, exit);
        try {
          runStep(fn, context, arguments, true);
        } catch (e) {
          isThrown = true;
          throw e;
        }
      };

      function exit() {
        if (flow.isTopLevel) {
          if (flow.err) {
            var message = 'An error must be handled in a last step of its flow. ' +
              'To indicate error handling completed, set null to `this.err` before exiting the last step. ' +
              'cause: ' + util.inspect(flow.err);
            var err = new Error(message);
            err.name = 'NueUnhandledError';
            throw err;
          }
        } else {
          if (flow.err) {
            flow.context.endWith.call(flow.context, flow.err);
          } else {
            flow.context.next.apply(flow.context, arguments);
          }
        }
      }
    }

    function runStep(step, context, args) {
      if (debugMode) {
        debug('BEGIN flow: %s(%d), step: %s(%d), args:',
          context.flowName || '<anonymous>', context._flow.flowId,
          context.stepName || '<anonymous>', context.stepIndex, Array.prototype.slice.call(args));
      }
      try {
        step.apply(context, args);
      } catch (e) {
        if (debugMode) {
          debug('ERROR flow: %s(%d), step: %s(%d), err:',
            context.flowName || '<anonymous>', context._flow.flowId,
            context.stepName || '<anonymous>', context.stepIndex, e);
        }
        throw e;
      }
    }
  }
}

function parallel() {
  if (arguments.length === 1 && typeof arguments[0] === 'string') {
    var flowName = arguments[0];
    return function () {
      return deferParallel(flowName, normalizeArgs(arguments));
    };
  } else {
    return deferParallel('', normalizeArgs(arguments));
  }

  function deferParallel(flowName, functions) {
    function startParallel() {
      assert(this instanceof StepContext, 'A `parallel` must be inside a flow or another parallel.');
      var self = this;
      var args = Array.prototype.slice.call(arguments);
      self.asyncEach(1)(functions, function (fn, group) {
        assert.equal(typeof fn, 'function', 'Each argument for `parallel` must be a function.');
        var callback = group.async(As.ALL);
        var end = function end() {
          if (this.err) {
            self.endWith(this.err);
          } else {
            callback.apply(null, arguments);
          }
        };
        end.stepName = (fn.stepName || fn.name) + '_end';
        flow(flowName)(fn, end).apply(self, args);
      });
    }
    startParallel.stepName = flowName;
    return startParallel;
  }
}

function as(index) {
  return new As(index);
}

function normalizeArgs(args) {
  return (args.length === 1 && Array.isArray(args[0])) ? args[0] : Array.prototype.slice.call(args);
}

function getLocation(target) {
  var originalPrepareStackTrace = Error.prepareStackTrace;
  var originalStackTraceLimit = Error.stackTraceLimit;
  Error.prepareStackTrace = prepareStackTrace;
  Error.stackTraceLimit = 1;
  var err = {};
  Error.captureStackTrace(err, target);
  var stack = err.stack;
  Error.prepareStackTrace = originalPrepareStackTrace;
  Error.stackTraceLimit = originalStackTraceLimit;
  return {
    functionName: stack.getFunctionName(),
    fileName: stack.getFileName(),
    lineNumber: stack.getLineNumber(),
    columnNumber: stack.getColumnNumber()
  };

  function prepareStackTrace() {
    return arguments[1][0];
  }
}

function debug() {
  var args = Array.prototype.slice.call(arguments);
  args[0] = 'NUE ' + args[0];
  console.error.apply(console, args);
}

function As(index) {
  this.index = index;
}

As.ALL = {description: 'all'};

function StepContext(flow, name, index, next, last) {
  this._flow = flow;
  this._next = next;
  this._last = last;
  this._async = new Async(false, this.next.bind(this), this.endWith.bind(this));
  this.err = flow.err;
  this.data = flow.data;
  this.flowName = flow.flowName;
  this.stepName = name;
  this.stepIndex = index;
}

StepContext.DEFAULT_CONCURRENCY = 10;

StepContext.noop = function noop() {};

StepContext.prototype._disable = function _disable() {
  this.next = StepContext.noop;
  this.end = StepContext.noop;
  this.endWith = StepContext.noop;
};

StepContext.prototype.next = function next() {
  this._disable();
  this._flow.err = this.err;
  this._next.apply(null, arguments);
};

StepContext.prototype.end = function end() {
  this._disable();
  this._flow.err = this.err;
  this._last.apply(null, arguments);
};

StepContext.prototype.endWith = function endWith(err) {
  this._disable();
  this._flow.err = err;
  this._last.call(null);
};

StepContext.prototype.async = function async(mapping) {
  return this._async.makeCallback.call(this._async, mapping, async);
};

StepContext.prototype.asyncEach = function asyncEach() {
  var self = this;
  var callback = self.async(As.ALL);
  var asyncObj = new Async(true, callback.bind(self), self.endWith.bind(self));
  var group = {
    async: function async(mapping) {
      return asyncObj.makeCallback.call(asyncObj, mapping, async);
    }
  };

  if (arguments.length === 1 && typeof arguments[0] === 'number') {
    var concurrency = arguments[0];
    return function () {
      validateAndStart(concurrency, arguments[0], arguments[1]);
    };
  } else {
    validateAndStart(StepContext.DEFAULT_CONCURRENCY, arguments[0], arguments[1]);
  }

  function validateAndStart(concurrency, array, worker) {
    assert(Array.isArray(array), 'An argument `array` must be an array.');
    assert.equal(typeof worker, 'function', 'An argument `worker` must be a function.');
    process.nextTick(function startEach() {
      each(concurrency, array, worker, 0);
    });
  }

  function each(concurrency, array, worker, index) {
    var len = array.length;
    for (var i = 0; i < concurrency && index < len; i++, index++) {
      worker.call(self, array[index], group, index, array);
    }
    if (index === len) {
      process.nextTick(asyncObj.makeCallback(Async.SIGNAL_UNLOCK));
    } else {
      process.nextTick(function nextEach() {
        each(concurrency, array, worker, index);
      });
    }
  }
};

StepContext.prototype.exec = function exec(fn) {
  assert(arguments.length > 1, 'Arguments length must be more than 1.');
  assert.equal(typeof fn, 'function', 'The first argument must be a function.');
  assert.equal(typeof arguments[arguments.length - 1], 'function', 'The last argument must be a function.');
  var self = this;
  var callback = arguments[arguments.length - 1];
  var args = Array.prototype.slice.call(arguments, 1, arguments.length - 1);
  var end = function end() {
    callback.apply(null, [this.err].concat(Array.prototype.slice.call(arguments)));
  };
  end.stepName = (fn.stepName || fn.name) + '_end';
  process.nextTick(function execFlow() {
    flow('exec')(fn, end).apply(self, args);
  });
};

function Async(lock, next, endWith) {
  this.lock = lock;
  this.next = next;
  this.endWith = endWith;
  this.index = 0;
  this.pending = 0;
  this.isCanceled = false;
  this.results = [];
}

Async.SIGNAL_UNLOCK = {description: 'signal_unlock'};

Async.prototype.makeCallback = function makeCallback(mapping, caller) {
  assert(typeof mapping === 'object' && mapping !== null, 'An argument `mapping` must be an object');
  this.pending++;
  if (this.index === 0 && !this.lock) {
    this.lock = true;
    process.nextTick(this.makeCallback(Async.SIGNAL_UNLOCK));
  }
  var index = mapping === Async.SIGNAL_UNLOCK ? -1 : this.index++;
  var location = getLocation(caller);
  var self = this;

  return function asyncCallback(err) {
    self.pending--;
    if (!self.isCanceled) {
      if (err && isErrorHandleRequired(mapping)) {
        self.isCanceled = true;
        self.endWith.call(null, makeAsyncError(err));
      } else {
        if (mapping === Async.SIGNAL_UNLOCK) {
          self.lock = false;
        } else {
          self.results[index] = mapArguments(mapping, arguments);
        }
        if (self.pending === 0 && !self.lock) {
          self.next.apply(null, self.results);
        }
      }
    }
  };

  function isErrorHandleRequired(mapping) {
    if (mapping === Async.SIGNAL_UNLOCK || mapping === As.ALL) {
      return false;
    }
    if (mapping instanceof As) {
      return mapping.index !== 0;
    }
    return Object.keys(mapping).every(function (key) {
      var value = mapping[key];
      if (value instanceof As) {
        return value.index !== 0;
      }
      return true;
    });
  }

  function mapArguments(mapping, args) {
    if (mapping === As.ALL) {
      return Array.prototype.slice.call(args);
    }
    if (mapping instanceof As) {
      return args[mapping.index];
    }
    return Object.keys(mapping).reduce(function (result, key) {
      var value = mapping[key];
      if (value instanceof As) {
        result[key] = args[value.index];
      } else {
        result[key] = value;
      }
      return result;
    }, {});
  }

  function makeAsyncError(err) {
    var log = {
      'function': location.functionName,
      location: util.format('%s:%d:%d', location.fileName, location.lineNumber, location.columnNumber),
      mapping: mapping
    };
    var cause;
    var history;
    if (err.name === 'NueAsyncError') {
      cause = err.cause;
      history = err.asyncCallHistory.concat([log]);
    } else {
      cause = err;
      history = [log];
    }
    var e = new Error('An error occurred in an async call.');
    e.name = 'NueAsyncError';
    e.cause = cause;
    e.asyncCallHistory = history;
    e.message += util.format('\ncause stack is ...\n  %s\nasync call history is ...\n',
      e.cause.stack, history);
    return e;
  }
};
<|endoftext|>


<commit_before>// Native
const { resolve } = require('path')

// Packages
const flatten = require('arr-flatten')
const unique = require('array-unique')
const ignore = require('ignore')
const _glob = require('glob')
const { stat, readdir, readFile } = require('fs-extra')

// Utilities
const IGNORED = require('./ignored')
const getLocalConfigPath = require('../../../config/local-path')

const glob = async function(pattern, options) {
  return new Promise((resolve, reject) => {
    _glob(pattern, options, (error, files) => {
      if (error) {
        reject(error)
      } else {
        resolve(files)
      }
    })
  })
}

/**
 * Remove leading `./` from the beginning of ignores
 * because our parser doesn't like them :|
 */

const clearRelative = function(str) {
  return str.replace(/(\n|^)\.\//g, '$1')
}

/**
 * Returns the contents of a file if it exists.
 *
 * @return {String} results or `''`
 */

const maybeRead = async function(path, default_ = '') {
  try {
    return await readFile(path, 'utf8')
  } catch (err) {
    return default_
  }
}

/**
 * Transform relative paths into absolutes,
 * and maintains absolutes as such.
 *
 * @param {String} maybe relative path
 * @param {String} parent full path
 */

const asAbsolute = function(path, parent) {
  if (path[0] === '/') {
    return path
  }

  return resolve(parent, path)
}

/**
 * Returns a list of files in the given
 * directory that are subject to be
 * synchronized for static deployments.
 *
 * @param {String} full path to directory
 * @param {Object} options:
 *  - `limit` {Number|null} byte limit
 *  - `debug` {Boolean} warn upon ignore
 * @return {Array} comprehensive list of paths to sync
 */

async function staticFiles(
  path,
  nowConfig = {},
  { limit = null, hasNowJson = false, debug = false } = {}
) {
  const whitelist = nowConfig.files

  // The package.json `files` whitelist still
  // honors ignores: https://docs.npmjs.com/files/package.json#files
  const search_ = whitelist || ['.']
  // Convert all filenames into absolute paths
  const search = Array.prototype.concat.apply(
    [],
    await Promise.all(
      search_.map(file => glob(file, { cwd: path, absolute: true, dot: true }))
    )
  )

  // Compile list of ignored patterns and files
  const gitIgnore = await maybeRead(resolve(path, '.gitignore'))

  const filter = ignore()
    .add(IGNORED + '\n' + clearRelative(gitIgnore))
    .createFilter()

  const prefixLength = path.length + 1

  // The package.json `files` whitelist still
  // honors npmignores: https://docs.npmjs.com/files/package.json#files
  // but we don't ignore if the user is explicitly listing files
  // under the now namespace, or using files in combination with gitignore
  const accepts = file => {
    const relativePath = file.substr(prefixLength)

    if (relativePath === '') {
      return true
    }

    const accepted = filter(relativePath)
    if (!accepted && debug) {
      console.log('> [debug] ignoring "%s"', file)
    }
    return accepted
  }

  // Locate files
  if (debug) {
    console.time(`> [debug] locating files ${path}`)
  }

  const files = await explode(search, {
    accepts,
    limit,
    debug
  })

  if (debug) {
    console.timeEnd(`> [debug] locating files ${path}`)
  }

  if (hasNowJson) {
    files.push(asAbsolute(getLocalConfigPath(path), path))
  }

  // Get files
  return unique(files)
}

/**
 * Returns a list of files in the given
 * directory that are subject to be
 * synchronized for npm.
 *
 * @param {String} full path to directory
 * @param {String} contents of `package.json` to avoid lookup
 * @param {Object} options:
 *  - `limit` {Number|null} byte limit
 *  - `debug` {Boolean} warn upon ignore
 * @return {Array} comprehensive list of paths to sync
 */

async function npm(
  path,
  pkg = {},
  nowConfig = {},
  { limit = null, hasNowJson = false, debug = false } = {}
) {
  const whitelist = nowConfig.files || pkg.files || (pkg.now && pkg.now.files)

  // The package.json `files` whitelist still
  // honors ignores: https://docs.npmjs.com/files/package.json#files
  const search_ = whitelist || ['.']
  // Convert all filenames into absolute paths
  const search = Array.prototype.concat.apply(
    [],
    await Promise.all(
      search_.map(file => glob(file, { cwd: path, absolute: true, dot: true }))
    )
  )

  // Compile list of ignored patterns and files
  const npmIgnore = await maybeRead(resolve(path, '.npmignore'), null)
  const gitIgnore =
    npmIgnore === null ? await maybeRead(resolve(path, '.gitignore')) : null

  const filter = ignore()
    .add(
      IGNORED + '\n' + clearRelative(npmIgnore === null ? gitIgnore : npmIgnore)
    )
    .createFilter()

  const prefixLength = path.length + 1

  // The package.json `files` whitelist still
  // honors npmignores: https://docs.npmjs.com/files/package.json#files
  // but we don't ignore if the user is explicitly listing files
  // under the now namespace, or using files in combination with gitignore
  const overrideIgnores =
    (pkg.now && pkg.now.files) ||
    nowConfig.files ||
    (gitIgnore !== null && pkg.files)
  const accepts = overrideIgnores
    ? () => true
    : file => {
        const relativePath = file.substr(prefixLength)

        if (relativePath === '') {
          return true
        }

        const accepted = filter(relativePath)
        if (!accepted && debug) {
          console.log('> [debug] ignoring "%s"', file)
        }
        return accepted
      }

  // Locate files
  if (debug) {
    console.time(`> [debug] locating files ${path}`)
  }

  const files = await explode(search, {
    accepts,
    limit,
    debug
  })

  if (debug) {
    console.timeEnd(`> [debug] locating files ${path}`)
  }

  // Always include manifest as npm does not allow ignoring it
  // source: https://docs.npmjs.com/files/package.json#files
  files.push(asAbsolute('package.json', path))

  if (hasNowJson) {
    files.push(asAbsolute(getLocalConfigPath(path), path))
  }

  // Get files
  return unique(files)
}

/**
 * Returns a list of files in the given
 * directory that are subject to be
 * sent to docker as build context.
 *
 * @param {String} full path to directory
 * @param {String} contents of `Dockerfile`
 * @param {Object} options:
 *  - `limit` {Number|null} byte limit
 *  - `debug` {Boolean} warn upon ignore
 * @return {Array} comprehensive list of paths to sync
 */

async function docker(
  path,
  nowConfig = {},
  { limit = null, hasNowJson = false, debug = false } = {}
) {
  const whitelist = nowConfig.files

  // Base search path
  // the now.json `files` whitelist still
  // honors ignores: https://docs.npmjs.com/files/package.json#files
  const search_ = whitelist || ['.']

  // Convert all filenames into absolute paths
  const search = search_.map(file => asAbsolute(file, path))

  // Compile list of ignored patterns and files
  const dockerIgnore = await maybeRead(resolve(path, '.dockerignore'), null)

  const filter = ignore()
    .add(
      IGNORED +
        '\n' +
        clearRelative(
          dockerIgnore === null
            ? await maybeRead(resolve(path, '.gitignore'))
            : dockerIgnore
        )
    )
    .createFilter()

  const prefixLength = path.length + 1
  const accepts = function(file) {
    const relativePath = file.substr(prefixLength)

    if (relativePath === '') {
      return true
    }

    const accepted = filter(relativePath)
    if (!accepted && debug) {
      console.log('> [debug] ignoring "%s"', file)
    }
    return accepted
  }

  // Locate files
  if (debug) {
    console.time(`> [debug] locating files ${path}`)
  }

  const files = await explode(search, { accepts, limit, debug })

  if (debug) {
    console.timeEnd(`> [debug] locating files ${path}`)
  }

  // Always include manifest as npm does not allow ignoring it
  // source: https://docs.npmjs.com/files/package.json#files
  files.push(asAbsolute('Dockerfile', path))

  if (hasNowJson) {
    files.push(asAbsolute(getLocalConfigPath(path), path))
  }

  // Get files
  return unique(files)
}

/**
 * Explodes directories into a full list of files.
 * Eg:
 *   in:  ['/a.js', '/b']
 *   out: ['/a.js', '/b/c.js', '/b/d.js']
 *
 * @param {Array} of {String}s representing paths
 * @param {Array} of ignored {String}s.
 * @param {Object} options:
 *  - `limit` {Number|null} byte limit
 *  - `debug` {Boolean} warn upon ignore
 * @return {Array} of {String}s of full paths
 */

async function explode(paths, { accepts, debug }) {
  const list = async file => {
    let path = file
    let s

    if (!accepts(file)) {
      return null
    }

    try {
      s = await stat(path)
    } catch (e) {
      // In case the file comes from `files`
      // and it wasn't specified with `.js` by the user
      path = file + '.js'

      try {
        s = await stat(path)
      } catch (e2) {
        if (debug) {
          console.log('> [debug] ignoring invalid file "%s"', file)
        }
        return null
      }
    }

    if (s.isDirectory()) {
      const all = await readdir(file)
      /* eslint-disable no-use-before-define */
      return many(all.map(subdir => asAbsolute(subdir, file)))
      /* eslint-enable no-use-before-define */
    } else if (!s.isFile()) {
      if (debug) {
        console.log('> [debug] ignoring special file "%s"', file)
      }
      return null
    }

    return path
  }

  const many = all => Promise.all(all.map(file => list(file)))
  return flatten(await many(paths)).filter(v => v !== null)
}

module.exports = {
  npm,
  docker,
  staticFiles
}
<commit_msg>Check if the `files` config property is an array (#858)<commit_after>// Native
const { resolve } = require('path')

// Packages
const flatten = require('arr-flatten')
const unique = require('array-unique')
const ignore = require('ignore')
const _glob = require('glob')
const { stat, readdir, readFile } = require('fs-extra')

// Utilities
const IGNORED = require('./ignored')
const getLocalConfigPath = require('../../../config/local-path')

const glob = async function(pattern, options) {
  return new Promise((resolve, reject) => {
    _glob(pattern, options, (error, files) => {
      if (error) {
        reject(error)
      } else {
        resolve(files)
      }
    })
  })
}

/**
 * Remove leading `./` from the beginning of ignores
 * because our parser doesn't like them :|
 */

const clearRelative = function(str) {
  return str.replace(/(\n|^)\.\//g, '$1')
}

/**
 * Returns the contents of a file if it exists.
 *
 * @return {String} results or `''`
 */

const maybeRead = async function(path, default_ = '') {
  try {
    return await readFile(path, 'utf8')
  } catch (err) {
    return default_
  }
}

/**
 * Transform relative paths into absolutes,
 * and maintains absolutes as such.
 *
 * @param {String} maybe relative path
 * @param {String} parent full path
 */

const asAbsolute = function(path, parent) {
  if (path[0] === '/') {
    return path
  }

  return resolve(parent, path)
}

/**
 * Returns a list of files in the given
 * directory that are subject to be
 * synchronized for static deployments.
 *
 * @param {String} full path to directory
 * @param {Object} options:
 *  - `limit` {Number|null} byte limit
 *  - `debug` {Boolean} warn upon ignore
 * @return {Array} comprehensive list of paths to sync
 */

async function staticFiles(
  path,
  nowConfig = {},
  { limit = null, hasNowJson = false, debug = false } = {}
) {
  const whitelist = nowConfig.files

  // The package.json `files` whitelist still
  // honors ignores: https://docs.npmjs.com/files/package.json#files
  const search_ = Array.isArray(whitelist) ? whitelist : ['.']
  // Convert all filenames into absolute paths
  const search = Array.prototype.concat.apply(
    [],
    await Promise.all(
      search_.map(file => glob(file, { cwd: path, absolute: true, dot: true }))
    )
  )

  // Compile list of ignored patterns and files
  const gitIgnore = await maybeRead(resolve(path, '.gitignore'))

  const filter = ignore()
    .add(IGNORED + '\n' + clearRelative(gitIgnore))
    .createFilter()

  const prefixLength = path.length + 1

  // The package.json `files` whitelist still
  // honors npmignores: https://docs.npmjs.com/files/package.json#files
  // but we don't ignore if the user is explicitly listing files
  // under the now namespace, or using files in combination with gitignore
  const accepts = file => {
    const relativePath = file.substr(prefixLength)

    if (relativePath === '') {
      return true
    }

    const accepted = filter(relativePath)
    if (!accepted && debug) {
      console.log('> [debug] ignoring "%s"', file)
    }
    return accepted
  }

  // Locate files
  if (debug) {
    console.time(`> [debug] locating files ${path}`)
  }

  const files = await explode(search, {
    accepts,
    limit,
    debug
  })

  if (debug) {
    console.timeEnd(`> [debug] locating files ${path}`)
  }

  if (hasNowJson) {
    files.push(asAbsolute(getLocalConfigPath(path), path))
  }

  // Get files
  return unique(files)
}

/**
 * Returns a list of files in the given
 * directory that are subject to be
 * synchronized for npm.
 *
 * @param {String} full path to directory
 * @param {String} contents of `package.json` to avoid lookup
 * @param {Object} options:
 *  - `limit` {Number|null} byte limit
 *  - `debug` {Boolean} warn upon ignore
 * @return {Array} comprehensive list of paths to sync
 */

async function npm(
  path,
  pkg = {},
  nowConfig = {},
  { limit = null, hasNowJson = false, debug = false } = {}
) {
  const whitelist = nowConfig.files || pkg.files || (pkg.now && pkg.now.files)

  // The package.json `files` whitelist still
  // honors ignores: https://docs.npmjs.com/files/package.json#files
  const search_ = whitelist || ['.']
  // Convert all filenames into absolute paths
  const search = Array.prototype.concat.apply(
    [],
    await Promise.all(
      search_.map(file => glob(file, { cwd: path, absolute: true, dot: true }))
    )
  )

  // Compile list of ignored patterns and files
  const npmIgnore = await maybeRead(resolve(path, '.npmignore'), null)
  const gitIgnore =
    npmIgnore === null ? await maybeRead(resolve(path, '.gitignore')) : null

  const filter = ignore()
    .add(
      IGNORED + '\n' + clearRelative(npmIgnore === null ? gitIgnore : npmIgnore)
    )
    .createFilter()

  const prefixLength = path.length + 1

  // The package.json `files` whitelist still
  // honors npmignores: https://docs.npmjs.com/files/package.json#files
  // but we don't ignore if the user is explicitly listing files
  // under the now namespace, or using files in combination with gitignore
  const overrideIgnores =
    (pkg.now && pkg.now.files) ||
    nowConfig.files ||
    (gitIgnore !== null && pkg.files)
  const accepts = overrideIgnores
    ? () => true
    : file => {
        const relativePath = file.substr(prefixLength)

        if (relativePath === '') {
          return true
        }

        const accepted = filter(relativePath)
        if (!accepted && debug) {
          console.log('> [debug] ignoring "%s"', file)
        }
        return accepted
      }

  // Locate files
  if (debug) {
    console.time(`> [debug] locating files ${path}`)
  }

  const files = await explode(search, {
    accepts,
    limit,
    debug
  })

  if (debug) {
    console.timeEnd(`> [debug] locating files ${path}`)
  }

  // Always include manifest as npm does not allow ignoring it
  // source: https://docs.npmjs.com/files/package.json#files
  files.push(asAbsolute('package.json', path))

  if (hasNowJson) {
    files.push(asAbsolute(getLocalConfigPath(path), path))
  }

  // Get files
  return unique(files)
}

/**
 * Returns a list of files in the given
 * directory that are subject to be
 * sent to docker as build context.
 *
 * @param {String} full path to directory
 * @param {String} contents of `Dockerfile`
 * @param {Object} options:
 *  - `limit` {Number|null} byte limit
 *  - `debug` {Boolean} warn upon ignore
 * @return {Array} comprehensive list of paths to sync
 */

async function docker(
  path,
  nowConfig = {},
  { limit = null, hasNowJson = false, debug = false } = {}
) {
  const whitelist = nowConfig.files

  // Base search path
  // the now.json `files` whitelist still
  // honors ignores: https://docs.npmjs.com/files/package.json#files
  const search_ = whitelist || ['.']

  // Convert all filenames into absolute paths
  const search = search_.map(file => asAbsolute(file, path))

  // Compile list of ignored patterns and files
  const dockerIgnore = await maybeRead(resolve(path, '.dockerignore'), null)

  const filter = ignore()
    .add(
      IGNORED +
        '\n' +
        clearRelative(
          dockerIgnore === null
            ? await maybeRead(resolve(path, '.gitignore'))
            : dockerIgnore
        )
    )
    .createFilter()

  const prefixLength = path.length + 1
  const accepts = function(file) {
    const relativePath = file.substr(prefixLength)

    if (relativePath === '') {
      return true
    }

    const accepted = filter(relativePath)
    if (!accepted && debug) {
      console.log('> [debug] ignoring "%s"', file)
    }
    return accepted
  }

  // Locate files
  if (debug) {
    console.time(`> [debug] locating files ${path}`)
  }

  const files = await explode(search, { accepts, limit, debug })

  if (debug) {
    console.timeEnd(`> [debug] locating files ${path}`)
  }

  // Always include manifest as npm does not allow ignoring it
  // source: https://docs.npmjs.com/files/package.json#files
  files.push(asAbsolute('Dockerfile', path))

  if (hasNowJson) {
    files.push(asAbsolute(getLocalConfigPath(path), path))
  }

  // Get files
  return unique(files)
}

/**
 * Explodes directories into a full list of files.
 * Eg:
 *   in:  ['/a.js', '/b']
 *   out: ['/a.js', '/b/c.js', '/b/d.js']
 *
 * @param {Array} of {String}s representing paths
 * @param {Array} of ignored {String}s.
 * @param {Object} options:
 *  - `limit` {Number|null} byte limit
 *  - `debug` {Boolean} warn upon ignore
 * @return {Array} of {String}s of full paths
 */

async function explode(paths, { accepts, debug }) {
  const list = async file => {
    let path = file
    let s

    if (!accepts(file)) {
      return null
    }

    try {
      s = await stat(path)
    } catch (e) {
      // In case the file comes from `files`
      // and it wasn't specified with `.js` by the user
      path = file + '.js'

      try {
        s = await stat(path)
      } catch (e2) {
        if (debug) {
          console.log('> [debug] ignoring invalid file "%s"', file)
        }
        return null
      }
    }

    if (s.isDirectory()) {
      const all = await readdir(file)
      /* eslint-disable no-use-before-define */
      return many(all.map(subdir => asAbsolute(subdir, file)))
      /* eslint-enable no-use-before-define */
    } else if (!s.isFile()) {
      if (debug) {
        console.log('> [debug] ignoring special file "%s"', file)
      }
      return null
    }

    return path
  }

  const many = all => Promise.all(all.map(file => list(file)))
  return flatten(await many(paths)).filter(v => v !== null)
}

module.exports = {
  npm,
  docker,
  staticFiles
}
<|endoftext|>


<commit_before>import $RefParser from 'json-schema-ref-parser';

import Container from './class/Container';
import format from './api/format';
import option from './api/option';
import env from './core/constants';
import random from './core/random';
import utils from './core/utils';
import run from './core/run';

const container = new Container();

function setupKeywords() {
  // built-in support
  container.define('pattern', random.randexp);

  // safe auto-increment values
  container.define('autoIncrement', function autoIncrement(value, schema) {
    if (!this.offset) {
      const min = schema.minimum || 1;
      const max = min + env.MAX_NUMBER;
      const offset = value.initialOffset || schema.initialOffset;

      this.offset = offset || random.number(min, max);
    }

    if (value === true) {
      return this.offset++; // eslint-disable-line
    }

    return schema;
  });

  // safe-and-sequential dates
  container.define('sequentialDate', function sequentialDate(value, schema) {
    if (!this.now) {
      this.now = random.date();
    }

    if (value) {
      schema = this.now.toISOString();
      value = value === true
        ? 'days'
        : value;

      if (['seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years'].indexOf(value) === -1) {
        throw new Error(`Unsupported increment by ${utils.short(value)}`);
      }

      this.now.setTime(this.now.getTime() + random.date(value));
    }

    return schema;
  });
}

function getRefs(refs) {
  let $refs = {};

  if (Array.isArray(refs)) {
    refs.forEach(schema => {
      $refs[schema.$id || schema.id] = schema;
    });
  } else {
    $refs = refs || {};
  }

  return $refs;
}

const jsf = (schema, refs, cwd) => {
  console.log('[json-schema-faker] calling JsonSchemaFaker() is deprecated, call either .generate() or .resolve()');

  if (cwd) {
    console.log('[json-schema-faker] references are only supported by calling .resolve()');
  }

  return jsf.generate(schema, refs);
};

jsf.generate = (schema, refs) => {
  const $refs = getRefs(refs);

  return run($refs, schema, container);
};

jsf.resolve = (schema, refs, cwd) => {
  if (typeof refs === 'string') {
    cwd = refs;
    refs = {};
  }

  // normalize basedir (browser aware)
  cwd = cwd || (typeof process !== 'undefined' ? process.cwd() : '');
  cwd = `${cwd.replace(/\/+$/, '')}/`;

  const $refs = getRefs(refs);

  // identical setup as json-schema-sequelizer
  const fixedRefs = {
    order: 300,
    canRead: true,
    read(file, callback) {
      try {
        callback(null, $refs[file.url] || $refs[file.url.split('/').pop()]);
      } catch (e) {
        callback(e);
      }
    },
  };

  return $RefParser
    .dereference(cwd, schema, {
      resolve: {
        file: { order: 100 },
        http: { order: 200 },
        fixedRefs,
      },
      dereference: {
        circular: 'ignore',
      },
    }).then(sub => run($refs, sub, container));
};

setupKeywords();

jsf.format = format;
jsf.option = option;
jsf.random = random;

// returns itself for chaining
jsf.extend = (name, cb) => {
  container.extend(name, cb);
  return jsf;
};

jsf.define = (name, cb) => {
  container.define(name, cb);
  return jsf;
};

jsf.reset = name => {
  container.reset(name);
  setupKeywords();
  return jsf;
};

jsf.locate = name => {
  return container.get(name);
};

jsf.version = '0.5.0-rc16';

export default jsf;
<commit_msg>Inject VERSION<commit_after>import $RefParser from 'json-schema-ref-parser';

import Container from './class/Container';
import format from './api/format';
import option from './api/option';
import env from './core/constants';
import random from './core/random';
import utils from './core/utils';
import run from './core/run';

const container = new Container();

function setupKeywords() {
  // built-in support
  container.define('pattern', random.randexp);

  // safe auto-increment values
  container.define('autoIncrement', function autoIncrement(value, schema) {
    if (!this.offset) {
      const min = schema.minimum || 1;
      const max = min + env.MAX_NUMBER;
      const offset = value.initialOffset || schema.initialOffset;

      this.offset = offset || random.number(min, max);
    }

    if (value === true) {
      return this.offset++; // eslint-disable-line
    }

    return schema;
  });

  // safe-and-sequential dates
  container.define('sequentialDate', function sequentialDate(value, schema) {
    if (!this.now) {
      this.now = random.date();
    }

    if (value) {
      schema = this.now.toISOString();
      value = value === true
        ? 'days'
        : value;

      if (['seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years'].indexOf(value) === -1) {
        throw new Error(`Unsupported increment by ${utils.short(value)}`);
      }

      this.now.setTime(this.now.getTime() + random.date(value));
    }

    return schema;
  });
}

function getRefs(refs) {
  let $refs = {};

  if (Array.isArray(refs)) {
    refs.forEach(schema => {
      $refs[schema.$id || schema.id] = schema;
    });
  } else {
    $refs = refs || {};
  }

  return $refs;
}

const jsf = (schema, refs, cwd) => {
  console.log('[json-schema-faker] calling JsonSchemaFaker() is deprecated, call either .generate() or .resolve()');

  if (cwd) {
    console.log('[json-schema-faker] references are only supported by calling .resolve()');
  }

  return jsf.generate(schema, refs);
};

jsf.generate = (schema, refs) => {
  const $refs = getRefs(refs);

  return run($refs, schema, container);
};

jsf.resolve = (schema, refs, cwd) => {
  if (typeof refs === 'string') {
    cwd = refs;
    refs = {};
  }

  // normalize basedir (browser aware)
  cwd = cwd || (typeof process !== 'undefined' ? process.cwd() : '');
  cwd = `${cwd.replace(/\/+$/, '')}/`;

  const $refs = getRefs(refs);

  // identical setup as json-schema-sequelizer
  const fixedRefs = {
    order: 300,
    canRead: true,
    read(file, callback) {
      try {
        callback(null, $refs[file.url] || $refs[file.url.split('/').pop()]);
      } catch (e) {
        callback(e);
      }
    },
  };

  return $RefParser
    .dereference(cwd, schema, {
      resolve: {
        file: { order: 100 },
        http: { order: 200 },
        fixedRefs,
      },
      dereference: {
        circular: 'ignore',
      },
    }).then(sub => run($refs, sub, container));
};

setupKeywords();

jsf.format = format;
jsf.option = option;
jsf.random = random;

// returns itself for chaining
jsf.extend = (name, cb) => {
  container.extend(name, cb);
  return jsf;
};

jsf.define = (name, cb) => {
  container.define(name, cb);
  return jsf;
};

jsf.reset = name => {
  container.reset(name);
  setupKeywords();
  return jsf;
};

jsf.locate = name => {
  return container.get(name);
};

/* global VERSION */
jsf.version = VERSION;

export default jsf;
<|endoftext|>


<commit_before>'use strict';

var util = require('util');
var chainlib = require('../');
var log = chainlib.log;
var EventEmitter = require('events').EventEmitter;
var BitcoreP2P = require('bitcore-p2p');
var Pool = BitcoreP2P.Pool;
var Messages = BitcoreP2P.Messages;
var Inventory = BitcoreP2P.Inventory;
var bitcore = require('bitcore');
var _ = bitcore.deps._;
var BufferReader = bitcore.encoding.BufferReader;

function P2P(options) {
  var self = this;

  if(!options) {
    options = {};
  }

  this.messages = new Messages(options);
  this.pool = new Pool({
    network: options.network,
    addrs: options.addrs,
    messages: this.messages,
    dnsSeed: options.dnsSeed
  });
  this.db = options.db;
  this.chain = options.chain;
  this.noListen = _.isUndefined(options.noListen) ? P2P.NO_LISTEN : options.noListen;
  this.maxBlocks = options.maxBlocks || P2P.MAX_BLOCKS;
  this.staleTipAge = options.staleTipAge || P2P.STALE_TIP_AGE;
  this.syncTimeout = options.syncTimeout || P2P.SYNC_TIMEOUT;
  this.ready = false;
  this.synced = false;

  this.on('synced', function() {
    self.chain.lastSavedMetadataThreshold = 0;
    self.chain.saveMetadata();
  });
}

util.inherits(P2P, EventEmitter);

P2P.NO_LISTEN = false;
P2P.MAX_BLOCKS = 500;
P2P.STALE_TIP_AGE = 20 * 60 * 1000;
P2P.SYNC_TIMEOUT = 30000;

P2P.prototype.initialize = function() {
  var self = this;
  this.mempool = this.db.mempool;

  this.chain.lastSavedMetadataThreshold = 30000; // While syncing, only save metadata every 30 seconds

  this.chain.on('addblock', this._onChainAddBlock.bind(this));
  this.chain.on('queueprocessed', this._onChainQueueProcessed.bind(this));
  this.mempool.on('transaction', this._onMempoolTransaction.bind(this));
  this.pool.on('peerready', this._onPeerReady.bind(this));
  this.pool.on('peerinv', this._onPeerInv.bind(this));
  this.pool.on('peerblock', this._onPeerBlock.bind(this));
  this.pool.on('peertx', this._onPeerTx.bind(this));
  this.pool.on('peergetdata', this._onPeerGetData.bind(this));
  this.pool.on('peergetblocks', this._onPeerGetBlocks.bind(this));

  setImmediate(function() {
    self.pool.connect();
    if (!self.noListen) {
      self.pool.listen();
    }
  });

  this.emit('initialized');
};

P2P.prototype.sendMessage = function(message) {
  this.pool.sendMessage(message);
};

P2P.prototype._onPeerReady = function(peer) {
  log.info('Connected to peer', (peer.host + ':' + peer.port));

  this._sync(peer);

  if(!this.ready) {
    this.ready = true;
    this.emit('ready');
  }
};

P2P.prototype._onPeerInv = function(peer, message) {
  var self = this;

  log.debug('Received inv from ' + peer.host);

  var inventory = message.inventory;

  // check that we don't already have the data
  var filtered = inventory.filter(function(a) {
    if (a.type === Inventory.TYPE.TX) {
      return !self.mempool.hasTransaction(self._bufferToHash(a.hash));
    } else if (a.type === Inventory.TYPE.BLOCK) {
      return !self.mempool.hasBlock(self._bufferToHash(a.hash));
    } else {
      return false;
    }
  });

  if (filtered.length) {
    var message = self.messages.GetData(filtered);
    log.debug('Sent getdata message');
    peer.sendMessage(message);
  }
};

P2P.prototype._onPeerBlock = function(peer, message) {
  var self = this;

  var block = message.block;
  if(block && block.hash) {
    log.debug('Received block ' + block.hash + ' from ' + peer.host);
  } else {
    return log.debug('Received malformed block message from ' + peer.host, message);
  }

  this.mempool.addBlock(block);
};

P2P.prototype._onPeerTx = function(peer, message) {
  var transaction = message.transaction;
  if(transaction && transaction.hash) {
    log.debug('Received transaction ' + transaction.hash + ' from ' + peer.host);
  } else {
    log.debug('Received malformed transaction message from ' + peer.host, message);
  }
  this.mempool.addTransaction(transaction, function(err) {
    if(err) {
      log.error('Transaction ' + transaction.hash + ' failed to validate: ' + err);
    }
  });
};

P2P.prototype._onPeerGetData = function(peer, message) {
  var self = this;

  log.debug('Received getdata from ' + peer.host);
  var inventory = message.inventory;

  inventory.forEach(function(item) {
    if(item.type === Inventory.TYPE.BLOCK) {
      self.db.getBlock(self._bufferToHash(item.hash), function(err, block) {
        if(err) {
          return self.emit('error', err);
        } else if(!block) {
          message = self.messages.NotFound.forBlock(item.hash);
          return peer.sendMessage(message);
        }

        var message = self.messages.Block(block);
        peer.sendMessage(message);
      });
    } else if(item.type === Inventory.TYPE.TX) {

      var message;

      self.db.getTransaction(self._bufferToHash(item.hash), true, function(err, tx) {
        if(err) {
          return self.emit('error', err);
        } else if(!tx) {
          message = self.messages.NotFound.forTransaction(item.hash);
          return peer.sendMessage(message);
        }

        message = self.messages.Transaction(tx);
        peer.sendMessage(message);
      });
    }
  });
};

P2P.prototype._onChainAddBlock = function(block) {
  if(this.synced) {
    var message = this.messages.Inventory.forBlock(block.hash);
    this.pool.sendMessage(message);
  }
};

P2P.prototype._onChainQueueProcessed = function() {
  if(!this.synced) {
    this._sync();
  }
}

P2P.prototype._onMempoolTransaction = function(transaction) {
  var message = this.messages.Inventory.forTransaction(transaction.hash);
  this.pool.sendMessage(message);
};

P2P.prototype._bufferToHash = function(buffer) {
  return BufferReader(buffer).readReverse().toString('hex');
};

P2P.prototype._hashToBuffer = function(hash) {
  var buffer = new Buffer(hash, 'hex');
  return BufferReader(buffer).readReverse();
};

P2P.prototype._sync = function(peer) {
  var self = this;

  var tipHash = self.chain.tip.hash;

  if((self.chain.tip.timestamp.getTime() > (Date.now() - self.staleTipAge)) && !self.synced) {
    // Just emit synced immediately if the block is reasonably new
    self.synced = true;
    self.emit('synced');
  } else if(!self.synced) {
    // Emit synced if the tip does not change after 30 seconds
    setTimeout(function() {
      if(self.chain.tip.hash === tipHash && !self.synced) {
        self.synced = true;
        self.emit('synced');
      }
    }, self.syncTimeout);
  }

  self.chain.getHashes(self.chain.tip.hash, function(err, hashes) {
    if(err) {
      return self.emit('error', err);
    }

    if(!peer) {
      peer = self._getRandomPeer();
    }

    if(peer) {
      var getBlocksMessage = self._buildGetBlocksMessage(hashes);
      peer.sendMessage(getBlocksMessage);
      log.debug('Sent getblocks message');
    }
  });
};

P2P.prototype._getRandomPeer = function() {
  var peerCount = Object.keys(this.pool._connectedPeers).length;
  if(!peerCount) {
    return null;
  }
  var key = Object.keys(this.pool._connectedPeers)[Math.floor(Math.random() * peerCount)];
  return this.pool._connectedPeers[key];
};

P2P.prototype._buildGetBlocksMessage = function(hashes) {
  // Based off of https://en.bitcoin.it/wiki/Protocol_documentation

  var filtered = [];
  // Push last 10 indices first
  var step = 1;
  var start = 0;

  for(var i = hashes.length - 1; i > 0; i -= step, ++start) {
    if(start >= 10) {
      step *= 2;
    }

    filtered.push(this._hashToBuffer(hashes[i]));
  }
  filtered.push(this._hashToBuffer(hashes[0]));

  var message = this.messages.GetBlocks({
    starts: filtered,
    stop: new Buffer(Array(32))
  });

  return message;
};

P2P.prototype._onPeerGetBlocks = function(peer, message, callback) {
  var self = this;

  log.debug('Received getblocks from ' + peer.host);

  if(!callback) {
    callback = function() {};
  }

  // See if they are on the main chain via the block locator
  // Figure out all the blocks they need
  // Respond with an inv message
  var locatorHashes = message.starts.map(this._bufferToHash);
  var stopHash = message.stop.readUInt32LE(0) === 0 ? null : this._bufferToHash(message.stop);

  self.chain.getHashes(self.chain.tip.hash, function(err, hashes) {
    if(err) {
      return self.emit('error', err);
    }

    var hashesDictionary = {};

    // Convert hashes to dictionary (this can probably be cached too)
    for(var i = 0; i < hashes.length; i++) {
      hashesDictionary[hashes[i]] = i;
    }

    // Find first common ancestor
    var commonAncestor;
    var startHeight = 0;

    for(var i = 0; i < locatorHashes.length; i++) {
      if(hashesDictionary.hasOwnProperty(locatorHashes[i])) {
        commonAncestor = locatorHashes[i];
        startHeight = hashesDictionary[locatorHashes[i]] + 1;
        break;
      }
    }

    if(!commonAncestor) {
      // If none of the hashes match start with the genesis block
      commonAncestor = self.chain.genesis.hash;
    }

    var inventory = [];

    // Add up to MAX_BLOCKS to an inventory message
    for(var i = startHeight; i < startHeight + self.maxBlocks; i++) {
      if(i >= hashes.length || hashes[i] === stopHash) {
        break;
      }
      inventory.push(Inventory.forBlock(hashes[i]));
    }

    // Send an inventory message if we have any blocks that they need
    if(inventory.length) {
      var message = self.messages.Inventory(inventory);
      peer.sendMessage(message);
    }

    callback();
  });
};

module.exports = P2P;
<commit_msg>Ignore p2p transactions while syncing<commit_after>'use strict';

var util = require('util');
var chainlib = require('../');
var log = chainlib.log;
var EventEmitter = require('events').EventEmitter;
var BitcoreP2P = require('bitcore-p2p');
var Pool = BitcoreP2P.Pool;
var Messages = BitcoreP2P.Messages;
var Inventory = BitcoreP2P.Inventory;
var bitcore = require('bitcore');
var _ = bitcore.deps._;
var BufferReader = bitcore.encoding.BufferReader;

function P2P(options) {
  var self = this;

  if(!options) {
    options = {};
  }

  this.messages = new Messages(options);
  this.pool = new Pool({
    network: options.network,
    addrs: options.addrs,
    messages: this.messages,
    dnsSeed: options.dnsSeed
  });
  this.db = options.db;
  this.chain = options.chain;
  this.noListen = _.isUndefined(options.noListen) ? P2P.NO_LISTEN : options.noListen;
  this.maxBlocks = options.maxBlocks || P2P.MAX_BLOCKS;
  this.staleTipAge = options.staleTipAge || P2P.STALE_TIP_AGE;
  this.syncTimeout = options.syncTimeout || P2P.SYNC_TIMEOUT;
  this.ready = false;
  this.synced = false;
  this.ignoreTransactions = true; // Ignore p2p transactions while syncing

  this.on('synced', function() {
    self.ignoreTransactions = false;
    self.chain.lastSavedMetadataThreshold = 0;
    self.chain.saveMetadata();
  });
}

util.inherits(P2P, EventEmitter);

P2P.NO_LISTEN = false;
P2P.MAX_BLOCKS = 500;
P2P.STALE_TIP_AGE = 20 * 60 * 1000;
P2P.SYNC_TIMEOUT = 30000;

P2P.prototype.initialize = function() {
  var self = this;
  this.mempool = this.db.mempool;

  this.chain.lastSavedMetadataThreshold = 30000; // While syncing, only save metadata every 30 seconds

  this.chain.on('addblock', this._onChainAddBlock.bind(this));
  this.chain.on('queueprocessed', this._onChainQueueProcessed.bind(this));
  this.mempool.on('transaction', this._onMempoolTransaction.bind(this));
  this.pool.on('peerready', this._onPeerReady.bind(this));
  this.pool.on('peerinv', this._onPeerInv.bind(this));
  this.pool.on('peerblock', this._onPeerBlock.bind(this));
  this.pool.on('peertx', this._onPeerTx.bind(this));
  this.pool.on('peergetdata', this._onPeerGetData.bind(this));
  this.pool.on('peergetblocks', this._onPeerGetBlocks.bind(this));

  setImmediate(function() {
    self.pool.connect();
    if (!self.noListen) {
      self.pool.listen();
    }
  });

  this.emit('initialized');
};

P2P.prototype.sendMessage = function(message) {
  this.pool.sendMessage(message);
};

P2P.prototype._onPeerReady = function(peer) {
  log.info('Connected to peer', (peer.host + ':' + peer.port));

  this._sync(peer);

  if(!this.ready) {
    this.ready = true;
    this.emit('ready');
  }
};

P2P.prototype._onPeerInv = function(peer, message) {
  var self = this;

  log.debug('Received inv from ' + peer.host);

  var inventory = message.inventory;

  // check that we don't already have the data
  var filtered = inventory.filter(function(a) {
    if (a.type === Inventory.TYPE.TX && !self.ignoreTransactions) {
      return !self.mempool.hasTransaction(self._bufferToHash(a.hash));
    } else if (a.type === Inventory.TYPE.BLOCK) {
      return !self.mempool.hasBlock(self._bufferToHash(a.hash));
    } else {
      return false;
    }
  });

  if (filtered.length) {
    var message = self.messages.GetData(filtered);
    log.debug('Sent getdata message');
    peer.sendMessage(message);
  }
};

P2P.prototype._onPeerBlock = function(peer, message) {
  var self = this;

  var block = message.block;
  if(block && block.hash) {
    log.debug('Received block ' + block.hash + ' from ' + peer.host);
  } else {
    return log.debug('Received malformed block message from ' + peer.host, message);
  }

  this.mempool.addBlock(block);
};

P2P.prototype._onPeerTx = function(peer, message) {
  var transaction = message.transaction;
  if(transaction && transaction.hash) {
    log.debug('Received transaction ' + transaction.hash + ' from ' + peer.host);
  } else {
    log.debug('Received malformed transaction message from ' + peer.host, message);
  }
  this.mempool.addTransaction(transaction, function(err) {
    if(err) {
      log.error('Transaction ' + transaction.hash + ' failed to validate: ' + err);
    }
  });
};

P2P.prototype._onPeerGetData = function(peer, message) {
  var self = this;

  log.debug('Received getdata from ' + peer.host);
  var inventory = message.inventory;

  inventory.forEach(function(item) {
    if(item.type === Inventory.TYPE.BLOCK) {
      self.db.getBlock(self._bufferToHash(item.hash), function(err, block) {
        if(err) {
          return self.emit('error', err);
        } else if(!block) {
          message = self.messages.NotFound.forBlock(item.hash);
          return peer.sendMessage(message);
        }

        var message = self.messages.Block(block);
        peer.sendMessage(message);
      });
    } else if(item.type === Inventory.TYPE.TX) {

      var message;

      self.db.getTransaction(self._bufferToHash(item.hash), true, function(err, tx) {
        if(err) {
          return self.emit('error', err);
        } else if(!tx) {
          message = self.messages.NotFound.forTransaction(item.hash);
          return peer.sendMessage(message);
        }

        message = self.messages.Transaction(tx);
        peer.sendMessage(message);
      });
    }
  });
};

P2P.prototype._onChainAddBlock = function(block) {
  if(this.synced) {
    var message = this.messages.Inventory.forBlock(block.hash);
    this.pool.sendMessage(message);
  }
};

P2P.prototype._onChainQueueProcessed = function() {
  if(!this.synced) {
    this._sync();
  }
}

P2P.prototype._onMempoolTransaction = function(transaction) {
  var message = this.messages.Inventory.forTransaction(transaction.hash);
  this.pool.sendMessage(message);
};

P2P.prototype._bufferToHash = function(buffer) {
  return BufferReader(buffer).readReverse().toString('hex');
};

P2P.prototype._hashToBuffer = function(hash) {
  var buffer = new Buffer(hash, 'hex');
  return BufferReader(buffer).readReverse();
};

P2P.prototype._sync = function(peer) {
  var self = this;

  var tipHash = self.chain.tip.hash;

  if((self.chain.tip.timestamp.getTime() > (Date.now() - self.staleTipAge)) && !self.synced) {
    // Just emit synced immediately if the block is reasonably new
    self.synced = true;
    self.emit('synced');
  } else if(!self.synced) {
    // Emit synced if the tip does not change after 30 seconds
    setTimeout(function() {
      if(self.chain.tip.hash === tipHash && !self.synced) {
        self.synced = true;
        self.emit('synced');
      }
    }, self.syncTimeout);
  }

  self.chain.getHashes(self.chain.tip.hash, function(err, hashes) {
    if(err) {
      return self.emit('error', err);
    }

    if(!peer) {
      peer = self._getRandomPeer();
    }

    if(peer) {
      var getBlocksMessage = self._buildGetBlocksMessage(hashes);
      peer.sendMessage(getBlocksMessage);
      log.debug('Sent getblocks message');
    }
  });
};

P2P.prototype._getRandomPeer = function() {
  var peerCount = Object.keys(this.pool._connectedPeers).length;
  if(!peerCount) {
    return null;
  }
  var key = Object.keys(this.pool._connectedPeers)[Math.floor(Math.random() * peerCount)];
  return this.pool._connectedPeers[key];
};

P2P.prototype._buildGetBlocksMessage = function(hashes) {
  // Based off of https://en.bitcoin.it/wiki/Protocol_documentation

  var filtered = [];
  // Push last 10 indices first
  var step = 1;
  var start = 0;

  for(var i = hashes.length - 1; i > 0; i -= step, ++start) {
    if(start >= 10) {
      step *= 2;
    }

    filtered.push(this._hashToBuffer(hashes[i]));
  }
  filtered.push(this._hashToBuffer(hashes[0]));

  var message = this.messages.GetBlocks({
    starts: filtered,
    stop: new Buffer(Array(32))
  });

  return message;
};

P2P.prototype._onPeerGetBlocks = function(peer, message, callback) {
  var self = this;

  log.debug('Received getblocks from ' + peer.host);

  if(!callback) {
    callback = function() {};
  }

  // See if they are on the main chain via the block locator
  // Figure out all the blocks they need
  // Respond with an inv message
  var locatorHashes = message.starts.map(this._bufferToHash);
  var stopHash = message.stop.readUInt32LE(0) === 0 ? null : this._bufferToHash(message.stop);

  self.chain.getHashes(self.chain.tip.hash, function(err, hashes) {
    if(err) {
      return self.emit('error', err);
    }

    var hashesDictionary = {};

    // Convert hashes to dictionary (this can probably be cached too)
    for(var i = 0; i < hashes.length; i++) {
      hashesDictionary[hashes[i]] = i;
    }

    // Find first common ancestor
    var commonAncestor;
    var startHeight = 0;

    for(var i = 0; i < locatorHashes.length; i++) {
      if(hashesDictionary.hasOwnProperty(locatorHashes[i])) {
        commonAncestor = locatorHashes[i];
        startHeight = hashesDictionary[locatorHashes[i]] + 1;
        break;
      }
    }

    if(!commonAncestor) {
      // If none of the hashes match start with the genesis block
      commonAncestor = self.chain.genesis.hash;
    }

    var inventory = [];

    // Add up to MAX_BLOCKS to an inventory message
    for(var i = startHeight; i < startHeight + self.maxBlocks; i++) {
      if(i >= hashes.length || hashes[i] === stopHash) {
        break;
      }
      inventory.push(Inventory.forBlock(hashes[i]));
    }

    // Send an inventory message if we have any blocks that they need
    if(inventory.length) {
      var message = self.messages.Inventory(inventory);
      peer.sendMessage(message);
    }

    callback();
  });
};

module.exports = P2P;
<|endoftext|>


<commit_before>/**
 * JSONP request.
 */

var Promise = require('./promise');

module.exports = function (_, options) {

    var callback = '_jsonp' + Math.random().toString(36).substr(2), response = {}, script, body;

    options.params[options.jsonp] = callback;

    if (_.isFunction(options.beforeSend)) {

        _.warn('beforeSend has been deprecated in ^0.1.17. ' +
            'Use transformRequest instead.'
        );

        options.beforeSend.call(this, {}, options);
    }

    return new Promise(function (resolve) {

        script = document.createElement('script');
        script.src = _.url(options);
        script.type = 'text/javascript';
        script.async = true;

        window[callback] = function (data) {
            body = data;
        };

        var handler = function (event) {

            delete window[callback];
            document.body.removeChild(script);

            if (event.type === 'load' && !body) {
                event.type = 'error';
            }

            response.ok = event.type !== 'error';
            response.status = response.ok ? 200 : 404;
            response.responseText = body ? body : event.type;
            response.headers = "";

            resolve(response);
        };

        script.onload = handler;
        script.onerror = handler;

        if (options.timeout) {
            setTimeout(function () {
                var response = {};

                response.ok = false;
                response.type = 'timeout';
                response.status = 0;
                response.responseText = '';
                response.header = function () {return null};

                resolve(response);

            }, options.timeout);
        }

        document.body.appendChild(script);
    });

};
<commit_msg>update jsonp<commit_after>/**
 * JSONP request.
 */

var Promise = require('./promise');

module.exports = function (_, options) {

    var callback = '_jsonp' + Math.random().toString(36).substr(2), response = {}, script, body;

    options.params[options.jsonp] = callback;

    if (_.isFunction(options.beforeSend)) {

        _.warn('beforeSend has been deprecated in ^0.1.17. ' +
            'Use transformRequest instead.'
        );

        options.beforeSend.call(this, {}, options);
    }

    return new Promise(function (resolve) {

        script = document.createElement('script');
        script.src = _.url(options);
        script.type = 'text/javascript';
        script.async = true;

        window[callback] = function (data) {
            body = data;
        };

        var handler = function (event) {

            if (event.type === 'load') {
                delete window[callback];
                document.body.removeChild(script);
            }

            if (event.type === 'load' && !body) {
                event.type = 'error';
            }

            switch (event.type) {
                case 'load':
                    response.status = 200;
                    break;
                case 'error':
                    response.status = 404;
                    break;
                default:
                    response.status = 0;
            }

            response.ok = event.type === 'load';
            response.responseText = body ? body : '';
            response.header = function () {return null};

            resolve(response);
        };

        if (options.timeout) {
            setTimeout(function () {
                handler({type: 'timeout'});
            }, options.timeout);
        }

        script.onload = handler;
        script.onerror = handler;

        document.body.appendChild(script);
    });

};
<|endoftext|>


<commit_before>'use babel';

import fs from 'fs-extra';
import path from 'path';
import temp from 'temp';
import specHelpers from 'atom-build-spec-helpers';
import os from 'os';

describe('Keymap', () => {
  const originalHomedirFn = os.homedir;
  let directory = null;
  let workspaceElement = null;

  temp.track();

  beforeEach(() => {
    const createdHomeDir = temp.mkdirSync('atom-build-spec-home');
    os.homedir = () => createdHomeDir;
    directory = fs.realpathSync(temp.mkdirSync({ prefix: 'atom-build-spec-' })) + path.sep;
    atom.project.setPaths([ directory ]);

    atom.config.set('build.buildOnSave', false);
    atom.config.set('build.panelVisibility', 'Toggle');
    atom.config.set('build.saveOnBuild', false);
    atom.config.set('build.notificationOnRefresh', true);

    jasmine.unspy(window, 'setTimeout');
    jasmine.unspy(window, 'clearTimeout');

    runs(() => {
      workspaceElement = atom.views.getView(atom.workspace);
      workspaceElement.setAttribute('style', 'width:9999px');
      jasmine.attachToDOM(workspaceElement);
    });

    waitsForPromise(() => {
      return atom.packages.activatePackage('build');
    });
  });

  afterEach(() => {
    os.homedir = originalHomedirFn;
    fs.removeSync(directory);
  });

  describe('when custom keymap is defined in .atom-build.json', () => {
    it('should trigger the build when that key combination is pressed', () => {
      fs.writeFileSync(directory + '.atom-build.json', JSON.stringify({
        name: 'The default build',
        cmd: 'echo default',
        targets: {
          'keymapped build': {
            cmd: 'echo keymapped',
            keymap: 'ctrl-alt-k'
          }
        }
      }));

      runs(() => atom.commands.dispatch(workspaceElement, 'build:trigger'));

      waitsFor(() => {
        return workspaceElement.querySelector('.build .title') &&
          workspaceElement.querySelector('.build .title').classList.contains('success');
      });

      runs(() => {
        expect(workspaceElement.querySelector('.terminal').terminal.getContent()).toMatch(/default/);
        atom.commands.dispatch(workspaceElement, 'build:toggle-panel');
      });

      waitsFor(() => {
        return !workspaceElement.querySelector('.build .title');
      });

      runs(() => {
        specHelpers.keydown('k', { ctrl: true, alt: true, element: workspaceElement });
      });

      waitsFor(() => {
        return workspaceElement.querySelector('.build .title') &&
          workspaceElement.querySelector('.build .title').classList.contains('success');
      });

      runs(() => {
        expect(workspaceElement.querySelector('.terminal').terminal.getContent()).toMatch(/keymapped/);
      });
    });

    it('should not changed the set active build', () => {
      fs.writeFileSync(directory + '.atom-build.json', JSON.stringify({
        name: 'The default build',
        cmd: 'echo default',
        targets: {
          'keymapped build': {
            cmd: 'echo keymapped',
            keymap: 'ctrl-alt-k'
          }
        }
      }));

      runs(() => atom.commands.dispatch(workspaceElement, 'build:trigger'));

      waitsFor(() => {
        return workspaceElement.querySelector('.build .title') &&
          workspaceElement.querySelector('.build .title').classList.contains('success');
      });

      runs(() => {
        expect(workspaceElement.querySelector('.terminal').terminal.getContent()).toMatch(/default/);
        atom.commands.dispatch(workspaceElement, 'build:toggle-panel');
      });

      waitsFor(() => {
        return !workspaceElement.querySelector('.build .title');
      });

      runs(() => {
        specHelpers.keydown('k', { ctrl: true, alt: true, element: workspaceElement });
      });

      waitsFor(() => {
        return workspaceElement.querySelector('.build .title') &&
          workspaceElement.querySelector('.build .title').classList.contains('success');
      });

      runs(() => {
        expect(workspaceElement.querySelector('.terminal').terminal.getContent()).toMatch(/keymapped/);
        atom.commands.dispatch(workspaceElement, 'build:toggle-panel');
      });

      waitsFor(() => {
        return !workspaceElement.querySelector('.build .title');
      });

      runs(() => {
        atom.commands.dispatch(workspaceElement, 'build:trigger');
      });

      waitsFor(() => {
        return workspaceElement.querySelector('.build .title') &&
          workspaceElement.querySelector('.build .title').classList.contains('success');
      });

      runs(() => {
        expect(workspaceElement.querySelector('.terminal').terminal.getContent()).toMatch(/default/);
        atom.commands.dispatch(workspaceElement, 'build:toggle-panel');
      });
    });

    it('should dispose keymap when reloading targets', () => {
      fs.writeFileSync(directory + '.atom-build.json', JSON.stringify({
        name: 'The default build',
        cmd: 'echo default',
        targets: {
          'keymapped build': {
            cmd: 'echo keymapped',
            keymap: 'ctrl-alt-k'
          }
        }
      }));

      runs(() => atom.commands.dispatch(workspaceElement, 'build:trigger'));

      waitsFor(() => {
        return workspaceElement.querySelector('.build .title') &&
          workspaceElement.querySelector('.build .title').classList.contains('success');
      });

      runs(() => {
        expect(workspaceElement.querySelector('.terminal').terminal.getContent()).toMatch(/default/);
      });

      waitsFor(() => {
        return !workspaceElement.querySelector('.build .title');
      });

      runs(() => {
        specHelpers.keydown('k', { ctrl: true, alt: true, element: workspaceElement });
      });

      waitsFor(() => {
        return workspaceElement.querySelector('.build .title') &&
          workspaceElement.querySelector('.build .title').classList.contains('success');
      });

      runs(() => {
        expect(workspaceElement.querySelector('.terminal').terminal.getContent()).toMatch(/keymapped/);
        atom.commands.dispatch(workspaceElement, 'build:toggle-panel');
        fs.writeFileSync(directory + '.atom-build.json', JSON.stringify({
          name: 'The default build',
          cmd: 'echo default',
          targets: {
            'keymapped build': {
              cmd: 'echo ctrl-x new file',
              keymap: 'ctrl-x'
            }
          }
        }));
      });

      waitsForPromise(() => specHelpers.awaitTargets());

      waitsFor(() => {
        return !workspaceElement.querySelector('.build .title');
      });

      runs(() => {
        specHelpers.keydown('k', { ctrl: true, alt: true, element: workspaceElement });
      });

      waits(300);

      runs(() => {
        expect(workspaceElement.querySelector('.build')).not.toExist();
        specHelpers.keydown('x', { ctrl: true, element: workspaceElement });
      });

      waitsFor(() => {
        return workspaceElement.querySelector('.build .title') &&
            workspaceElement.querySelector('.build .title').classList.contains('success');
      });

      runs(() => {
        expect(workspaceElement.querySelector('.terminal').terminal.getContent()).toMatch(/ctrl-x new file/);
      });
    });
  });
});
<commit_msg>Fixing keymap specs<commit_after>'use babel';

import fs from 'fs-extra';
import path from 'path';
import temp from 'temp';
import os from 'os';

describe('Keymap', () => {
  const originalHomedirFn = os.homedir;
  let directory = null;
  let workspaceElement = null;

  temp.track();

  beforeEach(() => {
    const createdHomeDir = temp.mkdirSync('atom-build-spec-home');
    os.homedir = () => createdHomeDir;
    directory = fs.realpathSync(temp.mkdirSync({ prefix: 'atom-build-spec-' })) + path.sep;
    atom.project.setPaths([ directory ]);

    atom.config.set('build.buildOnSave', false);
    atom.config.set('build.panelVisibility', 'Toggle');
    atom.config.set('build.saveOnBuild', false);
    atom.config.set('build.notificationOnRefresh', true);

    jasmine.unspy(window, 'setTimeout');
    jasmine.unspy(window, 'clearTimeout');

    runs(() => {
      workspaceElement = atom.views.getView(atom.workspace);
      workspaceElement.setAttribute('style', 'width:9999px');
      jasmine.attachToDOM(workspaceElement);
    });

    waitsForPromise(() => {
      return atom.packages.activatePackage('build');
    });
  });

  afterEach(() => {
    os.homedir = originalHomedirFn;
    fs.removeSync(directory);
  });

  describe('when custom keymap is defined in .atom-build.json', () => {
    it('should trigger the build when that key combination is pressed', () => {
      fs.writeFileSync(directory + '.atom-build.json', JSON.stringify({
        name: 'The default build',
        cmd: 'echo default',
        targets: {
          'keymapped build': {
            cmd: 'echo keymapped',
            keymap: 'ctrl-alt-k'
          }
        }
      }));

      runs(() => atom.commands.dispatch(workspaceElement, 'build:trigger'));

      waitsFor(() => {
        return workspaceElement.querySelector('.build .title') &&
          workspaceElement.querySelector('.build .title').classList.contains('success');
      });

      runs(() => {
        const bindings = atom.keymaps.findKeyBindings({ command: 'build:trigger:Custom: keymapped build' });
        expect(bindings.length).toEqual(1);
        expect(bindings[0].keystrokes).toEqual('ctrl-alt-k');
      });
    });

    it('should dispose keymap when reloading targets', () => {
      fs.writeFileSync(directory + '.atom-build.json', JSON.stringify({
        name: 'The default build',
        cmd: 'echo default',
        targets: {
          'keymapped build': {
            cmd: 'echo keymapped',
            keymap: 'ctrl-alt-k'
          }
        }
      }));

      runs(() => atom.commands.dispatch(workspaceElement, 'build:trigger'));
      waitsFor(() => {
        return workspaceElement.querySelector('.build .title') &&
          workspaceElement.querySelector('.build .title').classList.contains('success');
      });

      runs(() => {
        const bindings = atom.keymaps.findKeyBindings({ command: 'build:trigger:Custom: keymapped build' });
        expect(bindings.length).toEqual(1);
        expect(bindings[0].keystrokes).toEqual('ctrl-alt-k');
      });

      runs(() => {
        atom.commands.dispatch(workspaceElement, 'build:toggle-panel');
        fs.writeFileSync(directory + '.atom-build.json', JSON.stringify({
          name: 'The default build',
          cmd: 'echo default',
          targets: {
            'keymapped build': {
              cmd: 'echo ctrl-x new file',
              keymap: 'ctrl-x'
            }
          }
        }));
      });

      runs(() => atom.commands.dispatch(workspaceElement, 'build:trigger'));
      waitsFor(() => {
        return workspaceElement.querySelector('.build .title') &&
          workspaceElement.querySelector('.build .title').classList.contains('success');
      });

      runs(() => {
        const bindings = atom.keymaps.findKeyBindings({ command: 'build:trigger:Custom: keymapped build' });
        expect(bindings.length).toEqual(1);
        expect(bindings[0].keystrokes).not.toEqual('ctrl-alt-x');
      });
    });
  });
});
<|endoftext|>


<commit_before>/*
 * Collapsible widget extension: set
 * https://github.com/filamentgroup/collapsible
 * Copyright (c) 2014 Filament Group, Inc.
 * Licensed under the MIT, GPL licenses.
 */

;(function ($, window, document, undefined) {
	var pluginName = "collapsible";

	// Simple auto-init by selector that runs when the dom is ready. Trigger "enhance" if desirable.
	$( document ).bind( "expand." + pluginName, function( e ){
		var pluginName = "collapsible";
		var setAttr = "data-" + pluginName + "-set";
		var selector = "." + pluginName + "[" + setAttr + "]";
		var $collapsible = $( e.target );
		if( $collapsible.is( selector ) ){
			var value = $collapsible.attr( setAttr );
			var $set = $( "." + pluginName + "[" + setAttr + "='" + value + "']" ).filter(function() {
				return this !== $collapsible[0];
			});

			$set.each(function(){
				$( this ).data( pluginName ).collapse();
			});
		}
	});

})(jQuery, window, document);
<commit_msg>fixes #28<commit_after>/*
 * Collapsible widget extension: set
 * https://github.com/filamentgroup/collapsible
 * Copyright (c) 2014 Filament Group, Inc.
 * Licensed under the MIT, GPL licenses.
 */

;(function ($, window, document, undefined) {
	var pluginName = "collapsible";

	// Simple auto-init by selector that runs when the dom is ready. Trigger "enhance" if desirable.
	$( document ).bind( "expand." + pluginName, function( e ){
		var pluginName = "collapsible";
		var setAttr = "data-" + pluginName + "-set";
		var selector = "." + pluginName + "[" + setAttr + "]";
		var $collapsible = $( e.target );
		if( $collapsible.is( selector ) ){
			var value = $collapsible.attr( setAttr );
			var $set = $( "." + pluginName + "[" + setAttr + "='" + value + "']" ).filter(function() {
				return this !== $collapsible[0];
			});

			$set.each(function(){
				$( this ).data( pluginName ).collapse();
			});
			var openItemTop = e.target.offsetTop;
			// from jquery...
			var scroll =  (function() {
				var prop = 'pageYOffset',
					method = 'scrollTop';
				return window ? (prop in window) ? window[ prop ] :
					window.document.documentElement[ method ] :
					window.document.body[ method ];
			}());

			if( scroll > openItemTop ){
				window.scrollTo( 0, openItemTop );
			}
		}
	});

})(jQuery, window, document);
<|endoftext|>


<commit_before>import m from 'mithril';
import _ from 'underscore';
import moment from 'moment';
import h from '../h';
import projectVM from '../vms/project-vm';
import projectRewardList from './project-reward-list';
import projectGoalsBox from './project-goals-box';
import projectReport from './project-report';
import projectSuggestedContributions from './project-suggested-contributions';

const projectAbout = {
    view(ctrl, args) {
        const project = args.project() || {},
            onlineDays = () => {
                const diff = moment(project.zone_online_date).diff(moment(project.zone_expires_at)),
                    duration = moment.duration(diff);

                return -Math.ceil(duration.asDays());
            };
        const fundingPeriod = () => (project.is_published && h.existy(project.zone_expires_at)) ? m('.funding-period', [
            m('.fontsize-small.fontweight-semibold.u-text-center-small-only', 'Período de campanha'),
            m('.fontsize-small.u-text-center-small-only', `${h.momentify(project.zone_online_date)} - ${h.momentify(project.zone_expires_at)} (${onlineDays()} dias)`)
        ]) : '';

        return m('#project-about', [
            m('.project-about.w-col.w-col-8', {
                config: h.UIHelper()
            }, [
                m('p.fontsize-base', [
                    m('strong', 'O projeto'),
                ]),
                m('.fontsize-base[itemprop="about"]', m.trust(h.selfOrEmpty(project.about_html, '...'))),
                project.budget ? [
                    m('p.fontsize-base.fontweight-semibold', 'Orçamento'),
                    m('p.fontsize-base', m.trust(project.budget))
                ] : '',
                m.component(projectReport)
            ]),
            m('.w-col.w-col-4.w-hidden-small.w-hidden-tiny', [projectVM.isSubscription(project) ? args.subscriptionData() ? m(projectGoalsBox, { goalDetails: args.goalDetails, subscriptionData: args.subscriptionData }) : h.loader() : '', !_.isEmpty(args.rewardDetails()) ? [
                m('.fontsize-base.fontweight-semibold.u-marginbottom-30', 'Recompensas'),
                m.component(projectRewardList, {
                    project: args.project,
                    hasSubscription: args.hasSubscription,
                    rewardDetails: args.rewardDetails
                }), fundingPeriod()
            ] : [
                m('.fontsize-base.fontweight-semibold.u-marginbottom-30', 'Sugestões de apoio'),
                m.component(projectSuggestedContributions, { project: args.project }),
                fundingPeriod()
            ]])
        ]);
    }
};

export default projectAbout;
<commit_msg>show 6x on top of contributions list<commit_after>import m from 'mithril';
import _ from 'underscore';
import moment from 'moment';
import h from '../h';
import projectVM from '../vms/project-vm';
import projectRewardList from './project-reward-list';
import projectGoalsBox from './project-goals-box';
import projectReport from './project-report';
import projectSuggestedContributions from './project-suggested-contributions';

const projectAbout = {
    view(ctrl, args) {
        const project = args.project() || {},
            onlineDays = () => {
                const diff = moment(project.zone_online_date).diff(moment(project.zone_expires_at)),
                    duration = moment.duration(diff);

                return -Math.ceil(duration.asDays());
            };
        const fundingPeriod = () => (project.is_published && h.existy(project.zone_expires_at)) ? m('.funding-period', [
            m('.fontsize-small.fontweight-semibold.u-text-center-small-only', 'Período de campanha'),
            m('.fontsize-small.u-text-center-small-only', `${h.momentify(project.zone_online_date)} - ${h.momentify(project.zone_expires_at)} (${onlineDays()} dias)`)
        ]) : '';

        const buildIfTrue = (condition, buildSteps) => condition ? buildSteps : '';

        const nextStepsCardOptions = () => {
            const isSubscription = projectVM.isSubscription(project);
            const hasRewards = !_.isEmpty(args.rewardDetails());
            const titleText = hasRewards ? 'Recompensas' : 'Sugestões de apoio';

            return [
                isSubscription ? [
                    m('.fontsize-base.fontweight-semibold.u-marginbottom-30', titleText),
                ] : [
                    m('.fontsize-base.u-marginbottom-30.w-hidden-small.w-hidden-tiny', [
                        m('span.fontweight-semibold', titleText),
                        m.trust(' '),
                        m("span.badge.fontsize-smaller.badge-success", "parcele em até 6x")
                    ])
                ],
                hasRewards ? [
                    m.component(projectRewardList, {
                        project: args.project,
                        hasSubscription: args.hasSubscription,
                        rewardDetails: args.rewardDetails
                    })
                ] : [
                    m.component(projectSuggestedContributions, { project: args.project })
                ],
                fundingPeriod()
            ];
        };

        return m('#project-about', [
            m('.project-about.w-col.w-col-8', {
                config: h.UIHelper()
            }, [
                m('p.fontsize-base', [
                    m('strong', 'O projeto'),
                ]),
                m('.fontsize-base[itemprop="about"]', m.trust(h.selfOrEmpty(project.about_html, '...'))),
                project.budget ? [
                    m('p.fontsize-base.fontweight-semibold', 'Orçamento'),
                    m('p.fontsize-base', m.trust(project.budget))
                ] : '',
                m.component(projectReport)
            ]),
            m('.w-col.w-col-4.w-hidden-small.w-hidden-tiny', [
                projectVM.isSubscription(project) ? (args.subscriptionData() ? m(projectGoalsBox, { goalDetails: args.goalDetails, subscriptionData: args.subscriptionData }) : h.loader()) : '',
                nextStepsCardOptions()
            ])
        ]);
    }
};

export default projectAbout;
<|endoftext|>