<commit_before>'use strict'

var util = require('util')
var hypercore = require('hypercore')
var swarm = require('hyperdrive-archive-swarm')
var DepDb = require('dependency-db')
var progress = require('progress-string')
var diff = require('ansi-diff-stream')()
var clean = require('normalize-registry-metadata')
var transform = require('parallel-transform')
var through = require('through2')
var mkdirp = require('mkdirp')
var afterAll = require('after-all')
var pump = require('pump')
var speedometer = require('speedometer')
var prettierBytes = require('prettier-bytes')
var db = require('./db')
var debug = require('debug')(require('../package').name)

module.exports = function (opts) {
  mkdirp.sync(db.path)
  console.log('cache location:', db.path)

  var lvlDb = db.level()
  var depDb = new DepDb(lvlDb)
  var key = opts.key || '503cf9e12d8ae49a07568d90f04bbdbfa3f1998ab97ed2b5143c1f3f69ec052f'
  var feed = hypercore(lvlDb).createFeed(key, {sparse: true})
  var downloadSpeed = speedometer()
  var processingSpeed = speedometer()

  var state = {
    startBlock: 0,
    currentBlock: 0,
    feedLength: 0,
    downloaded: 0,
    bps: 0,
    pps: 0,
    remaining: 0,
    processed: 0
  }

  swarm(feed)

  if (!debug.enabled) diff.pipe(process.stdout)

  lvlDb.get('!last_block!', function (err, lastBlock) {
    if (err && !err.notFound) throw err

    state.processed = lastBlock ? Number(lastBlock) : 0
    state.startBlock = state.currentBlock = lastBlock ? state.processed + 1 : 0
    trackSyncing()

    feed.open(function (err) {
      if (err) throw err

      debug('chache is open')

      state.feedLength = feed.blocks
      state.remaining = feed.blocksRemaining()
      printProgress()

      processChanges(state.startBlock)
    })
  })

  function processChanges (start) {
    debug('processing changes from block %d', start)

    pump(
      feed.createReadStream({start: start}),
      transform(10, processChange),
      through.obj(recordLastBlock),
      onEnd
    )

    function processChange (data, cb) {
      var block = state.currentBlock++
      var change = JSON.parse(data)
      var doc = change.doc
      clean(doc)

      var next = afterAll(function (err) {
        state.bps = downloadSpeed(data.length)
        cb(err, {block: block})
      })

      if (!doc.versions || doc.versions.length === 0) {
        debug('skipping %s - no versions detected', change.id)
        return
      }

      Object.keys(doc.versions).forEach(function (version) {
        var pkg = doc.versions[version]
        debug('storing %s@%s (%d dependencies)...', pkg.name, pkg.version, pkg.dependencies ? Object.keys(pkg.dependencies).length : 0)
        depDb.store(pkg, next())
      })
    }

    function recordLastBlock (data, enc, cb) {
      lvlDb.put('!last_block!', data.block, function (err) {
        state.processed = data.block
        state.pps = processingSpeed(1)
        printProgress()
        cb(err)
      })
    }

    function onEnd (err) {
      if (err) throw err
      printProgress()
      process.exit()
    }
  }

  function trackSyncing () {
    feed.on('download', function (block) {
      state.downloaded++
      if (feed.blocks !== state.feedLength) {
        var newBlocks = feed.blocks - state.feedLength
        state.newBlocks = newBlocks
        state.remaining += newBlocks
        state.feedLength = feed.blocks
      }
      printProgress()
    })

    setInterval(function () {
      state.bps = downloadSpeed(0)
      state.pps = processingSpeed(0)
      printProgress()
    }, 1000).unref()
  }

  function printProgress () {
    var processedCounter = state.processed - state.startBlock + 1
    var totalToProcess = state.feedLength - state.startBlock

    var sessionBar = progress({total: state.remaining})
    var processedBar = progress({total: totalToProcess})

    diff.write(
      util.format('downloading [%s] (%d/%d) %s/s\n', sessionBar(state.downloaded), state.downloaded, state.remaining, prettierBytes(state.bps)) +
      util.format('processing  [%s] (%d/%d) %s/sec', processedBar(processedCounter), processedCounter, totalToProcess, state.pps.toFixed(1))
    )
  }
}
<commit_msg>Improve debugging output<commit_after>'use strict'

var util = require('util')
var hypercore = require('hypercore')
var swarm = require('hyperdrive-archive-swarm')
var DepDb = require('dependency-db')
var progress = require('progress-string')
var diff = require('ansi-diff-stream')()
var clean = require('normalize-registry-metadata')
var transform = require('parallel-transform')
var through = require('through2')
var mkdirp = require('mkdirp')
var afterAll = require('after-all')
var pump = require('pump')
var speedometer = require('speedometer')
var prettierBytes = require('prettier-bytes')
var db = require('./db')
var debug = require('debug')(require('../package').name)

module.exports = function (opts) {
  mkdirp.sync(db.path)
  console.log('cache location:', db.path)

  var lvlDb = db.level()
  var depDb = new DepDb(lvlDb)
  var key = opts.key || '503cf9e12d8ae49a07568d90f04bbdbfa3f1998ab97ed2b5143c1f3f69ec052f'
  var feed = hypercore(lvlDb).createFeed(key, {sparse: true})
  var downloadSpeed = speedometer()
  var processingSpeed = speedometer()

  var state = {
    startBlock: 0,
    currentBlock: 0,
    feedLength: 0,
    downloaded: 0,
    bps: 0,
    pps: 0,
    remaining: 0,
    processed: 0
  }

  swarm(feed)

  if (!debug.enabled) diff.pipe(process.stdout)

  lvlDb.get('!last_block!', function (err, lastBlock) {
    if (err && !err.notFound) throw err

    state.processed = lastBlock ? Number(lastBlock) : 0
    state.startBlock = state.currentBlock = lastBlock ? state.processed + 1 : 0
    debug('last block read', state)
    trackSyncing()

    feed.open(function (err) {
      if (err) throw err

      debug('cache is open')

      state.feedLength = feed.blocks
      state.remaining = feed.blocksRemaining()
      printProgress()

      processChanges(state.startBlock)
    })
  })

  function processChanges (start) {
    debug('processing changes from block %d', start)

    pump(
      feed.createReadStream({start: start}),
      transform(10, processChange),
      through.obj(recordLastBlock),
      onEnd
    )

    function processChange (data, cb) {
      var block = state.currentBlock++
      var change = JSON.parse(data)
      var doc = change.doc
      clean(doc)

      var next = afterAll(function (err) {
        state.bps = downloadSpeed(data.length)
        cb(err, {block: block})
      })

      if (!doc.versions || doc.versions.length === 0) {
        debug('skipping %s - no versions detected', change.id)
        return
      }

      Object.keys(doc.versions).forEach(function (version) {
        var pkg = doc.versions[version]
        debug('storing %s@%s (%d dependencies)...', pkg.name, pkg.version, pkg.dependencies ? Object.keys(pkg.dependencies).length : 0)
        depDb.store(pkg, next())
      })
    }

    function recordLastBlock (data, enc, cb) {
      lvlDb.put('!last_block!', data.block, function (err) {
        state.processed = data.block
        state.pps = processingSpeed(1)
        printProgress()
        cb(err)
      })
    }

    function onEnd (err) {
      if (err) throw err
      printProgress()
      process.exit()
    }
  }

  function trackSyncing () {
    feed.on('download', function (block) {
      state.downloaded++
      if (feed.blocks !== state.feedLength) {
        var newBlocks = feed.blocks - state.feedLength
        state.newBlocks = newBlocks
        state.remaining += newBlocks
        state.feedLength = feed.blocks
        debug('feed.blocks changed', state)
      }
      printProgress()
    })

    setInterval(function () {
      state.bps = downloadSpeed(0)
      state.pps = processingSpeed(0)
      printProgress()
    }, 1000).unref()
  }

  function printProgress () {
    var processedCounter = state.processed - state.startBlock + 1
    var totalToProcess = state.feedLength - state.startBlock

    var sessionBar = progress({total: state.remaining})
    var processedBar = progress({total: totalToProcess})

    diff.write(
      util.format('downloading [%s] (%d/%d) %s/s\n', sessionBar(state.downloaded), state.downloaded, state.remaining, prettierBytes(state.bps)) +
      util.format('processing  [%s] (%d/%d) %s/sec', processedBar(processedCounter), processedCounter, totalToProcess, state.pps.toFixed(1))
    )
  }
}
<|endoftext|>


<commit_before>import React, { Component } from "react";
import PropTypes from "prop-types";
import ModulePresenter from "./presenters/ModulePresenter";

export default class Module extends Component {
  static propTypes = {
    /** Indicates this module is currently active */
    active: PropTypes.bool,
    /** Indicates a nested submodule is currently active */
    activeChildren: PropTypes.bool,
    /** Zero or more SideNav submodules */
    children: PropTypes.node,
    /** An instance of @hig/icon */
    icon: PropTypes.node,
    /** URL to navigate to when clicking this module */
    link: PropTypes.string,
    /** Indicates whether to display nested submodules */
    minimized: PropTypes.bool,
    /** Called when clicking on the collapse button */
    onClickCollapseButton: PropTypes.func,
    /** Called when clicking on the title */
    onClickTitle: PropTypes.func,
    /** Called when hovering over the title */
    onMouseOver: PropTypes.func,
    /** A label for rendering this Module */
    title: PropTypes.string.isRequired,
    /** Anchor target. Applicable only if link is provided */
    target: PropTypes.oneOf(["_self", "_blank", "_parent", "_top"])
  };

  static defaultProps = {
    minimized: false
  };

  constructor(props) {
    super(props);

    this.state = {
      minimized: props.minimized
    };
  }

  handleModuleMinimizeToggle = e =>
    this.setState(
      { minimized: !this.state.minimized },
      () =>
        this.props.onClickCollapseButton && this.props.onClickCollapseButton(e)
    );

  /**
   * @returns {PresenterBag}
   */
  createPresenterBag() {
    const { minimized } = this.state;

    return {
      minimized,
      onClickCollapseButton: this.handleModuleMinimizeToggle
    };
  }

  /**
   * @param {PresenterBag}
   */
  renderPresenter = presenterBag => {
    const presenterProps = { ...this.props, ...presenterBag };
    const { children, ...otherProps } = presenterProps;

    return <ModulePresenter {...otherProps}>{children}</ModulePresenter>;
  };

  render() {
    const presenterBag = this.createPresenterBag();
    const { renderPresenter } = this;

    return renderPresenter(presenterBag);
  }
}
<commit_msg>feat(side-nav): Clicking module title toggle minimized state<commit_after>import React, { Component } from "react";
import PropTypes from "prop-types";
import ModulePresenter from "./presenters/ModulePresenter";

export default class Module extends Component {
  static propTypes = {
    /** Indicates this module is currently active */
    active: PropTypes.bool,
    /** Indicates a nested submodule is currently active */
    activeChildren: PropTypes.bool,
    /** Zero or more SideNav submodules */
    children: PropTypes.node,
    /** An instance of @hig/icon */
    icon: PropTypes.node,
    /** URL to navigate to when clicking this module */
    link: PropTypes.string,
    /** Indicates whether to display nested submodules */
    minimized: PropTypes.bool,
    /** Called when clicking on the collapse button */
    onClickCollapseButton: PropTypes.func,
    /** Called when clicking on the title */
    onClickTitle: PropTypes.func,
    /** Called when hovering over the title */
    onMouseOver: PropTypes.func,
    /** A label for rendering this Module */
    title: PropTypes.string.isRequired,
    /** Anchor target. Applicable only if link is provided */
    target: PropTypes.oneOf(["_self", "_blank", "_parent", "_top"])
  };

  static defaultProps = {
    minimized: false
  };

  constructor(props) {
    super(props);

    this.state = {
      minimized: props.minimized
    };
  }

  handleModuleMinimizeToggle = e =>
    this.setState(
      { minimized: !this.state.minimized },
      () =>
        this.props.onClickCollapseButton && this.props.onClickCollapseButton(e)
    );

  /**
   * @returns {PresenterBag}
   */
  createPresenterBag() {
    const { minimized } = this.state;

    return {
      minimized,
      onClickCollapseButton: this.handleModuleMinimizeToggle,
      onClickTitle: this.handleModuleMinimizeToggle
    };
  }

  /**
   * @param {PresenterBag}
   */
  renderPresenter = presenterBag => {
    const presenterProps = { ...this.props, ...presenterBag };
    const { children, ...otherProps } = presenterProps;

    return <ModulePresenter {...otherProps}>{children}</ModulePresenter>;
  };

  render() {
    const presenterBag = this.createPresenterBag();
    const { renderPresenter } = this;

    return renderPresenter(presenterBag);
  }
}
<|endoftext|>


<commit_before>
var format = require('util').format
  , request = require('request')
  , async = require('async')

/**
 * The Trello class
 */
module.exports = function Trello(apiKey, authToken) {
  this.apiKey = apiKey;
  this.authToken = authToken;
}

/**
 * Fetches trello lists for the board with the given id
 *
 * Callback will be passed an error if there is one and the array of lists.
 *
 * @param String boardId ID of the board to get lists for
 * @param Function cb The callback
 * @return Function The list getter function
 */
Trello.prototype.getBoardLists = function(boardId, cb) {
  var apiKey = this.apiKey
    , authToken = this.authToken
    , uri = format('https://api.trello.com/1/boards/%s/lists?key=%s&token=%s', boardId, apiKey, authToken);
  request(uri, function(error, resp, body) {
    if(error) { return cb(error); }
    var lists;
    try {
      lists = JSON.parse(body);
    } catch(error) {
      return cb(error);
    }
    return cb(null, lists);
  });
};

/**
 * Fetches trello cards for the given lists
 *
 * Note that cards are not separated by list.
 *
 * @param Array lists
 * @param Function cb
 */
Trello.prototype.getListsCards = function(lists, cb) {
  var apiKey = this.apiKey
    , authToken = this.authToken;
  async.map(
    lists,
    function(list, cb) {
      var listId = list.id
        , uri = format('https://api.trello.com/1/lists/%s/cards?key=%s&token=%s', listId, apiKey, authToken);
      request(uri, function(error, resp, body) {
        if(error) { return cb(error); }
        var cards;
        try {
          cards = JSON.parse(body);
        } catch(error) {
          return cb(error);
        }
        return cb(null, cards);
      });
    },
    function(err, cardsAndCards) {
      if(err) { return cb(err); }
      var allCards = [];
      cardsAndCards.forEach(function(cards) {
        Array.prototype.push.apply(allCards, cards);
      });
      cb(null, allCards);
    }
  );
};
<commit_msg>feature: Add ability to extract board ids from the url<commit_after>
var format = require('util').format
  , request = require('request')
  , async = require('async')

/**
 * The Trello class
 */
var Trello = module.exports = function(apiKey, authToken) {
  this.apiKey = apiKey;
  this.authToken = authToken;
}

/**
 * Get a board id from its url
 *
 * @param {String} boardUrl The board url
 * @return {String} The board id
 */
var getBoardIdFromUrl = function(boardUrl) {
  var urlParts = boardUrl.split('/'), part;

  while(urlParts.length) {
    part = urlParts.shift();
    if('b' === part && urlParts.length) {
      return urlParts[0];
    }
  }

  var msg = format('Could not get board id from url: "%s"', boardUrl);
  throw new Error(msg);
};

Trello.getBoardIdFromUrl = getBoardIdFromUrl;
Trello.prototype.getBoardIdFromUrl = getBoardIdFromUrl;

/**
 * Fetches trello lists for the board with the given id
 *
 * Callback will be passed an error if there is one and the array of lists.
 *
 * @param {String} boardId ID of the board to get lists for
 * @param {Function} cb The callback
 * @return {Function} The list getter function
 */
Trello.prototype.getBoardLists = function(boardId, cb) {
  var apiKey = this.apiKey
    , authToken = this.authToken
    , uri = format('https://api.trello.com/1/boards/%s/lists?key=%s&token=%s', boardId, apiKey, authToken);
  request(uri, function(error, resp, body) {
    if(error) { return cb(error); }
    var lists;
    try {
      lists = JSON.parse(body);
    } catch(error) {
      return cb(error);
    }
    return cb(null, lists);
  });
};

/**
 * Fetches trello cards for the given lists
 *
 * Note that cards are not separated by list.
 *
 * @param {Array} lists
 * @param {Function} cb
 */
Trello.prototype.getListsCards = function(lists, cb) {
  var apiKey = this.apiKey
    , authToken = this.authToken;
  async.map(
    lists,
    function(list, cb) {
      var listId = list.id
        , uri = format('https://api.trello.com/1/lists/%s/cards?key=%s&token=%s', listId, apiKey, authToken);
      request(uri, function(error, resp, body) {
        if(error) { return cb(error); }
        var cards;
        try {
          cards = JSON.parse(body);
        } catch(error) {
          return cb(error);
        }
        return cb(null, cards);
      });
    },
    function(err, cardsAndCards) {
      if(err) { return cb(err); }
      var allCards = [];
      cardsAndCards.forEach(function(cards) {
        Array.prototype.push.apply(allCards, cards);
      });
      cb(null, allCards);
    }
  );
};

<|endoftext|>


<commit_before>
var __toString = Object.prototype.toString

module.exports = {

  /*
   * A more exact `typeof`
   *
   * @param {Object} value
   * @return {String} type
   */
  type: function( value ) {
    value = __toString.call( value );
    return value.substring( 8, value.length - 1 );
  },

  toKey: function( input ) {

    var self = this

    input = input + ''
    input = input.replace( /[^a-z]/gi, '_' )
    input = input.split( '_' )

    return input.map( function ( chunk, i ) {
      return ( i !== 0 )
        ? self.ucfirst( chunk )
        : chunk.toLowerCase()
    }).join( '' )

  },

  ucfirst: function( string ) {
    string = ''+string
    return string.charAt(0).toUpperCase() + string.substring(1)
  },

  /**
   * A dead simple way to do inheritance in JS
   *
   * Stolen from @izs:
   * https://github.com/isaacs/inherits
   *
   * @param  {Function} c     Child
   * @param  {Function} p     Parent
   * @param  {Object}   proto Prototype
   * @return {Undefined}
   */
  inherit: function( c, p, proto ) {
    var e = {}
    proto = proto || {}
    ;[ c.prototype, proto ].forEach( function (s) {
      Object.getOwnPropertyNames( s ).forEach(function (k) {
        e[k] = Object.getOwnPropertyDescriptor( s, k )
      })
    })
    c.prototype = Object.create( p.prototype, e )
    c.super = p
  },

}
<commit_msg>Removed unused lib/utopia<commit_after><|endoftext|>


<commit_before>// ============================================================
// Code-generation of template tags

// The `CodeGen` class currently has no instance state, but in theory
// it could be useful to track per-function state, like whether we
// need to emit `var self = this` or not.
var CodeGen = SpacebarsCompiler.CodeGen = function () {};

var builtInBlockHelpers = SpacebarsCompiler._builtInBlockHelpers = {
  'if': 'Blaze.If',
  'unless': 'Blaze.Unless',
  'with': 'Spacebars.With',
  'each': 'Blaze.Each',
  'let': 'Blaze.Let'
};

// Mapping of "macros" which, when preceded by `Template.`, expand
// to special code rather than following the lookup rules for dotted
// symbols.
var builtInTemplateMacros = {
  // `view` is a local variable defined in the generated render
  // function for the template in which `Template.contentBlock` or
  // `Template.elseBlock` is invoked.
  'contentBlock': 'view.templateContentBlock',
  'elseBlock': 'view.templateElseBlock',

  // Confusingly, this makes `{{> Template.dynamic}}` an alias
  // for `{{> __dynamic}}`, where "__dynamic" is the template that
  // implements the dynamic template feature.
  'dynamic': 'Template.__dynamic',

  'subscriptionsReady': 'view.templateInstance().subscriptionsReady()'
};

// A "reserved name" can't be used as a <template> name.  This
// function is used by the template file scanner.
//
// Note that the runtime imposes additional restrictions, for example
// banning the name "body" and names of built-in object properties
// like "toString".
SpacebarsCompiler.isReservedName = function (name) {
  return builtInBlockHelpers.hasOwnProperty(name) ||
    builtInTemplateMacros.hasOwnProperty(name);
};

var makeObjectLiteral = function (obj) {
  var parts = [];
  for (var k in obj)
    parts.push(BlazeTools.toObjectLiteralKey(k) + ': ' + obj[k]);
  return '{' + parts.join(', ') + '}';
};

_.extend(CodeGen.prototype, {
  codeGenTemplateTag: function (tag) {
    var self = this;
    if (tag.position === HTMLTools.TEMPLATE_TAG_POSITION.IN_START_TAG) {
      // Special dynamic attributes: `<div {{attrs}}>...`
      // only `tag.type === 'DOUBLE'` allowed (by earlier validation)
      return BlazeTools.EmitCode('function () { return ' +
          self.codeGenMustache(tag.path, tag.args, 'attrMustache')
          + '; }');
    } else {
      if (tag.type === 'DOUBLE' || tag.type === 'TRIPLE') {
        var code = self.codeGenMustache(tag.path, tag.args);
        if (tag.type === 'TRIPLE') {
          code = 'Spacebars.makeRaw(' + code + ')';
        }
        if (tag.position !== HTMLTools.TEMPLATE_TAG_POSITION.IN_ATTRIBUTE) {
          // Reactive attributes are already wrapped in a function,
          // and there's no fine-grained reactivity.
          // Anywhere else, we need to create a View.
          code = 'Blaze.View(' +
            BlazeTools.toJSLiteral('lookup:' + tag.path.join('.')) + ', ' +
            'function () { return ' + code + '; })';
        }
        return BlazeTools.EmitCode(code);
      } else if (tag.type === 'INCLUSION' || tag.type === 'BLOCKOPEN') {
        var path = tag.path;
        var args = tag.args;

        if (tag.type === 'BLOCKOPEN' &&
            builtInBlockHelpers.hasOwnProperty(path[0])) {
          // if, unless, with, each.
          //
          // If someone tries to do `{{> if}}`, we don't
          // get here, but an error is thrown when we try to codegen the path.

          // Note: If we caught these errors earlier, while scanning, we'd be able to
          // provide nice line numbers.
          if (path.length > 1)
            throw new Error("Unexpected dotted path beginning with " + path[0]);
          if (! args.length)
            throw new Error("#" + path[0] + " requires an argument");

          var dataCode = null;
          // #each has a special treatment as it features two different forms:
          // - {{#each people}}
          // - {{#each person in people}}
          if (path[0] === 'each' && args.length >= 2 && args[1][0] === 'PATH' &&
              args[1][1].length && args[1][1][0] === 'in') {
            // minimum conditions are met for each-in.  now validate this
            // isn't some weird case.
            var eachUsage = "Use either {{#each items}} or " +
                  "{{#each item in items}} form of #each.";
            var inArg = args[1];
            if (! (args.length === 3 && inArg[1].length === 1)) {
              // we don't have 3 space-separated parts after #each, or
              // inArg doesn't look like ['PATH',['in']]
              throw new Error("Malformed #each. " + eachUsage);
            }
            // split out the variable name and sequence arguments
            var variableArg = args[0];
            if (! (variableArg[0] === "PATH" && variableArg[1].length === 1 &&
                   variableArg[1][0].replace(/\./g, ''))) {
              throw new Error("Bad variable name in #each");
            }
            var variable = variableArg[1][0];
            dataCode = 'function () { return { _sequence: Spacebars.call(' +
              self.codeGenPath(args[2][1]) +
              '), _variable: ' + BlazeTools.toJSLiteral(variable) + ' }; }';
          } else if (path[0] === 'let') {
            var dataProps = {};
            _.each(args, function (arg) {
              if (arg.length !== 3) {
                // not a keyword arg (x=y)
                throw new Error("Incorrect form of #let");
              }
              var argKey = arg[2];
              dataProps[argKey] =
                'function () { return Spacebars.call(' +
                self.codeGenArgValue(arg) + '); }';
            });
            dataCode = makeObjectLiteral(dataProps);
          }

          if (! dataCode) {
            // `args` must exist (tag.args.length > 0)
            dataCode = self.codeGenInclusionDataFunc(args) || 'null';
          }

          // `content` must exist
          var contentBlock = (('content' in tag) ?
                              self.codeGenBlock(tag.content) : null);
          // `elseContent` may not exist
          var elseContentBlock = (('elseContent' in tag) ?
                                  self.codeGenBlock(tag.elseContent) : null);

          var callArgs = [dataCode, contentBlock];
          if (elseContentBlock)
            callArgs.push(elseContentBlock);

          return BlazeTools.EmitCode(
            builtInBlockHelpers[path[0]] + '(' + callArgs.join(', ') + ')');

        } else {
          var compCode = self.codeGenPath(path, {lookupTemplate: true});
          if (path.length > 1) {
            // capture reactivity
            compCode = 'function () { return Spacebars.call(' + compCode +
              '); }';
          }

          var dataCode = self.codeGenInclusionDataFunc(tag.args);
          var content = (('content' in tag) ?
                         self.codeGenBlock(tag.content) : null);
          var elseContent = (('elseContent' in tag) ?
                             self.codeGenBlock(tag.elseContent) : null);

          var includeArgs = [compCode];
          if (content) {
            includeArgs.push(content);
            if (elseContent)
              includeArgs.push(elseContent);
          }

          var includeCode =
                'Spacebars.include(' + includeArgs.join(', ') + ')';

          // calling convention compat -- set the data context around the
          // entire inclusion, so that if the name of the inclusion is
          // a helper function, it gets the data context in `this`.
          // This makes for a pretty confusing calling convention --
          // In `{{#foo bar}}`, `foo` is evaluated in the context of `bar`
          // -- but it's what we shipped for 0.8.0.  The rationale is that
          // `{{#foo bar}}` is sugar for `{{#with bar}}{{#foo}}...`.
          if (dataCode) {
            includeCode =
              'Blaze._TemplateWith(' + dataCode + ', function () { return ' +
              includeCode + '; })';
          }

          // XXX BACK COMPAT - UI is the old name, Template is the new
          if ((path[0] === 'UI' || path[0] === 'Template') &&
              (path[1] === 'contentBlock' || path[1] === 'elseBlock')) {
            // Call contentBlock and elseBlock in the appropriate scope
            includeCode = 'Blaze._InOuterTemplateScope(view, function () { return '
              + includeCode + '; })';
          }

          return BlazeTools.EmitCode(includeCode);
        }
      } else if (tag.type === 'ESCAPE') {
        return tag.value;
      } else {
        // Can't get here; TemplateTag validation should catch any
        // inappropriate tag types that might come out of the parser.
        throw new Error("Unexpected template tag type: " + tag.type);
      }
    }
  },

  // `path` is an array of at least one string.
  //
  // If `path.length > 1`, the generated code may be reactive
  // (i.e. it may invalidate the current computation).
  //
  // No code is generated to call the result if it's a function.
  //
  // Options:
  //
  // - lookupTemplate {Boolean} If true, generated code also looks in
  //   the list of templates. (After helpers, before data context).
  //   Used when generating code for `{{> foo}}` or `{{#foo}}`. Only
  //   used for non-dotted paths.
  codeGenPath: function (path, opts) {
    if (builtInBlockHelpers.hasOwnProperty(path[0]))
      throw new Error("Can't use the built-in '" + path[0] + "' here");
    // Let `{{#if Template.contentBlock}}` check whether this template was
    // invoked via inclusion or as a block helper, in addition to supporting
    // `{{> Template.contentBlock}}`.
    // XXX BACK COMPAT - UI is the old name, Template is the new
    if (path.length >= 2 &&
        (path[0] === 'UI' || path[0] === 'Template')
        && builtInTemplateMacros.hasOwnProperty(path[1])) {
      if (path.length > 2)
        throw new Error("Unexpected dotted path beginning with " +
                        path[0] + '.' + path[1]);
      return builtInTemplateMacros[path[1]];
    }

    var firstPathItem = BlazeTools.toJSLiteral(path[0]);
    var lookupMethod = 'lookup';
    if (opts && opts.lookupTemplate && path.length === 1)
      lookupMethod = 'lookupTemplate';
    var code = 'view.' + lookupMethod + '(' + firstPathItem + ')';

    if (path.length > 1) {
      code = 'Spacebars.dot(' + code + ', ' +
        _.map(path.slice(1), BlazeTools.toJSLiteral).join(', ') + ')';
    }

    return code;
  },

  // Generates code for an `[argType, argValue]` argument spec,
  // ignoring the third element (keyword argument name) if present.
  //
  // The resulting code may be reactive (in the case of a PATH of
  // more than one element) and is not wrapped in a closure.
  codeGenArgValue: function (arg) {
    var self = this;

    var argType = arg[0];
    var argValue = arg[1];

    var argCode;
    switch (argType) {
    case 'STRING':
    case 'NUMBER':
    case 'BOOLEAN':
    case 'NULL':
      argCode = BlazeTools.toJSLiteral(argValue);
      break;
    case 'PATH':
      argCode = self.codeGenPath(argValue);
      break;
    case 'EXPR':
      // The format of EXPR is ['EXPR', { type: 'EXPR', path: [...], args: { ... } }]
      argCode = self.codeGenMustache(argValue.path, argValue.args, 'dataMustache');
      break;
    default:
      // can't get here
      throw new Error("Unexpected arg type: " + argType);
    }

    return argCode;
  },

  // Generates a call to `Spacebars.fooMustache` on evaluated arguments.
  // The resulting code has no function literals and must be wrapped in
  // one for fine-grained reactivity.
  codeGenMustache: function (path, args, mustacheType) {
    var self = this;

    var nameCode = self.codeGenPath(path);
    var argCode = self.codeGenMustacheArgs(args);
    var mustache = (mustacheType || 'mustache');

    return 'Spacebars.' + mustache + '(' + nameCode +
      (argCode ? ', ' + argCode.join(', ') : '') + ')';
  },

  // returns: array of source strings, or null if no
  // args at all.
  codeGenMustacheArgs: function (tagArgs) {
    var self = this;

    var kwArgs = null; // source -> source
    var args = null; // [source]

    // tagArgs may be null
    _.each(tagArgs, function (arg) {
      var argCode = self.codeGenArgValue(arg);

      if (arg.length > 2) {
        // keyword argument (represented as [type, value, name])
        kwArgs = (kwArgs || {});
        kwArgs[arg[2]] = argCode;
      } else {
        // positional argument
        args = (args || []);
        args.push(argCode);
      }
    });

    // put kwArgs in options dictionary at end of args
    if (kwArgs) {
      args = (args || []);
      args.push('Spacebars.kw(' + makeObjectLiteral(kwArgs) + ')');
    }

    return args;
  },

  codeGenBlock: function (content) {
    return SpacebarsCompiler.codeGen(content);
  },

  codeGenInclusionDataFunc: function (args) {
    var self = this;

    var dataFuncCode = null;

    if (! args.length) {
      // e.g. `{{#foo}}`
      return null;
    } else if (args[0].length === 3) {
      // keyword arguments only, e.g. `{{> point x=1 y=2}}`
      var dataProps = {};
      _.each(args, function (arg) {
        var argKey = arg[2];
        dataProps[argKey] = 'Spacebars.call(' + self.codeGenArgValue(arg) + ')';
      });
      dataFuncCode = makeObjectLiteral(dataProps);
    } else if (args[0][0] !== 'PATH') {
      // literal first argument, e.g. `{{> foo "blah"}}`
      //
      // tag validation has confirmed, in this case, that there is only
      // one argument (`args.length === 1`)
      dataFuncCode = self.codeGenArgValue(args[0]);
    } else if (args.length === 1) {
      // one argument, must be a PATH
      dataFuncCode = 'Spacebars.call(' + self.codeGenPath(args[0][1]) + ')';
    } else {
      // Multiple positional arguments; treat them as a nested
      // "data mustache"
      dataFuncCode = self.codeGenMustache(args[0][1], args.slice(1),
                                          'dataMustache');
    }

    return 'function () { return ' + dataFuncCode + '; }';
  }

});
<commit_msg>Fix for preventing templates from having reserved names like 'body' and 'instance'<commit_after>// ============================================================
// Code-generation of template tags

// The `CodeGen` class currently has no instance state, but in theory
// it could be useful to track per-function state, like whether we
// need to emit `var self = this` or not.
var CodeGen = SpacebarsCompiler.CodeGen = function () {};

var builtInBlockHelpers = SpacebarsCompiler._builtInBlockHelpers = {
  'if': 'Blaze.If',
  'unless': 'Blaze.Unless',
  'with': 'Spacebars.With',
  'each': 'Blaze.Each',
  'let': 'Blaze.Let'
};

// Mapping of "macros" which, when preceded by `Template.`, expand
// to special code rather than following the lookup rules for dotted
// symbols.
var builtInTemplateMacros = {
  // `view` is a local variable defined in the generated render
  // function for the template in which `Template.contentBlock` or
  // `Template.elseBlock` is invoked.
  'contentBlock': 'view.templateContentBlock',
  'elseBlock': 'view.templateElseBlock',

  // Confusingly, this makes `{{> Template.dynamic}}` an alias
  // for `{{> __dynamic}}`, where "__dynamic" is the template that
  // implements the dynamic template feature.
  'dynamic': 'Template.__dynamic',

  'subscriptionsReady': 'view.templateInstance().subscriptionsReady()'
};

var additionalReservedNames = ["body", "toString", "instance"];

// A "reserved name" can't be used as a <template> name.  This
// function is used by the template file scanner.
//
// Note that the runtime imposes additional restrictions, for example
// banning the name "body" and names of built-in object properties
// like "toString".
SpacebarsCompiler.isReservedName = function (name) {
  return builtInBlockHelpers.hasOwnProperty(name) ||
    builtInTemplateMacros.hasOwnProperty(name) ||
    _.indexOf(additionalReservedNames, name) > -1;
};

var makeObjectLiteral = function (obj) {
  var parts = [];
  for (var k in obj)
    parts.push(BlazeTools.toObjectLiteralKey(k) + ': ' + obj[k]);
  return '{' + parts.join(', ') + '}';
};

_.extend(CodeGen.prototype, {
  codeGenTemplateTag: function (tag) {
    var self = this;
    if (tag.position === HTMLTools.TEMPLATE_TAG_POSITION.IN_START_TAG) {
      // Special dynamic attributes: `<div {{attrs}}>...`
      // only `tag.type === 'DOUBLE'` allowed (by earlier validation)
      return BlazeTools.EmitCode('function () { return ' +
          self.codeGenMustache(tag.path, tag.args, 'attrMustache')
          + '; }');
    } else {
      if (tag.type === 'DOUBLE' || tag.type === 'TRIPLE') {
        var code = self.codeGenMustache(tag.path, tag.args);
        if (tag.type === 'TRIPLE') {
          code = 'Spacebars.makeRaw(' + code + ')';
        }
        if (tag.position !== HTMLTools.TEMPLATE_TAG_POSITION.IN_ATTRIBUTE) {
          // Reactive attributes are already wrapped in a function,
          // and there's no fine-grained reactivity.
          // Anywhere else, we need to create a View.
          code = 'Blaze.View(' +
            BlazeTools.toJSLiteral('lookup:' + tag.path.join('.')) + ', ' +
            'function () { return ' + code + '; })';
        }
        return BlazeTools.EmitCode(code);
      } else if (tag.type === 'INCLUSION' || tag.type === 'BLOCKOPEN') {
        var path = tag.path;
        var args = tag.args;

        if (tag.type === 'BLOCKOPEN' &&
            builtInBlockHelpers.hasOwnProperty(path[0])) {
          // if, unless, with, each.
          //
          // If someone tries to do `{{> if}}`, we don't
          // get here, but an error is thrown when we try to codegen the path.

          // Note: If we caught these errors earlier, while scanning, we'd be able to
          // provide nice line numbers.
          if (path.length > 1)
            throw new Error("Unexpected dotted path beginning with " + path[0]);
          if (! args.length)
            throw new Error("#" + path[0] + " requires an argument");

          var dataCode = null;
          // #each has a special treatment as it features two different forms:
          // - {{#each people}}
          // - {{#each person in people}}
          if (path[0] === 'each' && args.length >= 2 && args[1][0] === 'PATH' &&
              args[1][1].length && args[1][1][0] === 'in') {
            // minimum conditions are met for each-in.  now validate this
            // isn't some weird case.
            var eachUsage = "Use either {{#each items}} or " +
                  "{{#each item in items}} form of #each.";
            var inArg = args[1];
            if (! (args.length === 3 && inArg[1].length === 1)) {
              // we don't have 3 space-separated parts after #each, or
              // inArg doesn't look like ['PATH',['in']]
              throw new Error("Malformed #each. " + eachUsage);
            }
            // split out the variable name and sequence arguments
            var variableArg = args[0];
            if (! (variableArg[0] === "PATH" && variableArg[1].length === 1 &&
                   variableArg[1][0].replace(/\./g, ''))) {
              throw new Error("Bad variable name in #each");
            }
            var variable = variableArg[1][0];
            dataCode = 'function () { return { _sequence: Spacebars.call(' +
              self.codeGenPath(args[2][1]) +
              '), _variable: ' + BlazeTools.toJSLiteral(variable) + ' }; }';
          } else if (path[0] === 'let') {
            var dataProps = {};
            _.each(args, function (arg) {
              if (arg.length !== 3) {
                // not a keyword arg (x=y)
                throw new Error("Incorrect form of #let");
              }
              var argKey = arg[2];
              dataProps[argKey] =
                'function () { return Spacebars.call(' +
                self.codeGenArgValue(arg) + '); }';
            });
            dataCode = makeObjectLiteral(dataProps);
          }

          if (! dataCode) {
            // `args` must exist (tag.args.length > 0)
            dataCode = self.codeGenInclusionDataFunc(args) || 'null';
          }

          // `content` must exist
          var contentBlock = (('content' in tag) ?
                              self.codeGenBlock(tag.content) : null);
          // `elseContent` may not exist
          var elseContentBlock = (('elseContent' in tag) ?
                                  self.codeGenBlock(tag.elseContent) : null);

          var callArgs = [dataCode, contentBlock];
          if (elseContentBlock)
            callArgs.push(elseContentBlock);

          return BlazeTools.EmitCode(
            builtInBlockHelpers[path[0]] + '(' + callArgs.join(', ') + ')');

        } else {
          var compCode = self.codeGenPath(path, {lookupTemplate: true});
          if (path.length > 1) {
            // capture reactivity
            compCode = 'function () { return Spacebars.call(' + compCode +
              '); }';
          }

          var dataCode = self.codeGenInclusionDataFunc(tag.args);
          var content = (('content' in tag) ?
                         self.codeGenBlock(tag.content) : null);
          var elseContent = (('elseContent' in tag) ?
                             self.codeGenBlock(tag.elseContent) : null);

          var includeArgs = [compCode];
          if (content) {
            includeArgs.push(content);
            if (elseContent)
              includeArgs.push(elseContent);
          }

          var includeCode =
                'Spacebars.include(' + includeArgs.join(', ') + ')';

          // calling convention compat -- set the data context around the
          // entire inclusion, so that if the name of the inclusion is
          // a helper function, it gets the data context in `this`.
          // This makes for a pretty confusing calling convention --
          // In `{{#foo bar}}`, `foo` is evaluated in the context of `bar`
          // -- but it's what we shipped for 0.8.0.  The rationale is that
          // `{{#foo bar}}` is sugar for `{{#with bar}}{{#foo}}...`.
          if (dataCode) {
            includeCode =
              'Blaze._TemplateWith(' + dataCode + ', function () { return ' +
              includeCode + '; })';
          }

          // XXX BACK COMPAT - UI is the old name, Template is the new
          if ((path[0] === 'UI' || path[0] === 'Template') &&
              (path[1] === 'contentBlock' || path[1] === 'elseBlock')) {
            // Call contentBlock and elseBlock in the appropriate scope
            includeCode = 'Blaze._InOuterTemplateScope(view, function () { return '
              + includeCode + '; })';
          }

          return BlazeTools.EmitCode(includeCode);
        }
      } else if (tag.type === 'ESCAPE') {
        return tag.value;
      } else {
        // Can't get here; TemplateTag validation should catch any
        // inappropriate tag types that might come out of the parser.
        throw new Error("Unexpected template tag type: " + tag.type);
      }
    }
  },

  // `path` is an array of at least one string.
  //
  // If `path.length > 1`, the generated code may be reactive
  // (i.e. it may invalidate the current computation).
  //
  // No code is generated to call the result if it's a function.
  //
  // Options:
  //
  // - lookupTemplate {Boolean} If true, generated code also looks in
  //   the list of templates. (After helpers, before data context).
  //   Used when generating code for `{{> foo}}` or `{{#foo}}`. Only
  //   used for non-dotted paths.
  codeGenPath: function (path, opts) {
    if (builtInBlockHelpers.hasOwnProperty(path[0]))
      throw new Error("Can't use the built-in '" + path[0] + "' here");
    // Let `{{#if Template.contentBlock}}` check whether this template was
    // invoked via inclusion or as a block helper, in addition to supporting
    // `{{> Template.contentBlock}}`.
    // XXX BACK COMPAT - UI is the old name, Template is the new
    if (path.length >= 2 &&
        (path[0] === 'UI' || path[0] === 'Template')
        && builtInTemplateMacros.hasOwnProperty(path[1])) {
      if (path.length > 2)
        throw new Error("Unexpected dotted path beginning with " +
                        path[0] + '.' + path[1]);
      return builtInTemplateMacros[path[1]];
    }

    var firstPathItem = BlazeTools.toJSLiteral(path[0]);
    var lookupMethod = 'lookup';
    if (opts && opts.lookupTemplate && path.length === 1)
      lookupMethod = 'lookupTemplate';
    var code = 'view.' + lookupMethod + '(' + firstPathItem + ')';

    if (path.length > 1) {
      code = 'Spacebars.dot(' + code + ', ' +
        _.map(path.slice(1), BlazeTools.toJSLiteral).join(', ') + ')';
    }

    return code;
  },

  // Generates code for an `[argType, argValue]` argument spec,
  // ignoring the third element (keyword argument name) if present.
  //
  // The resulting code may be reactive (in the case of a PATH of
  // more than one element) and is not wrapped in a closure.
  codeGenArgValue: function (arg) {
    var self = this;

    var argType = arg[0];
    var argValue = arg[1];

    var argCode;
    switch (argType) {
    case 'STRING':
    case 'NUMBER':
    case 'BOOLEAN':
    case 'NULL':
      argCode = BlazeTools.toJSLiteral(argValue);
      break;
    case 'PATH':
      argCode = self.codeGenPath(argValue);
      break;
    case 'EXPR':
      // The format of EXPR is ['EXPR', { type: 'EXPR', path: [...], args: { ... } }]
      argCode = self.codeGenMustache(argValue.path, argValue.args, 'dataMustache');
      break;
    default:
      // can't get here
      throw new Error("Unexpected arg type: " + argType);
    }

    return argCode;
  },

  // Generates a call to `Spacebars.fooMustache` on evaluated arguments.
  // The resulting code has no function literals and must be wrapped in
  // one for fine-grained reactivity.
  codeGenMustache: function (path, args, mustacheType) {
    var self = this;

    var nameCode = self.codeGenPath(path);
    var argCode = self.codeGenMustacheArgs(args);
    var mustache = (mustacheType || 'mustache');

    return 'Spacebars.' + mustache + '(' + nameCode +
      (argCode ? ', ' + argCode.join(', ') : '') + ')';
  },

  // returns: array of source strings, or null if no
  // args at all.
  codeGenMustacheArgs: function (tagArgs) {
    var self = this;

    var kwArgs = null; // source -> source
    var args = null; // [source]

    // tagArgs may be null
    _.each(tagArgs, function (arg) {
      var argCode = self.codeGenArgValue(arg);

      if (arg.length > 2) {
        // keyword argument (represented as [type, value, name])
        kwArgs = (kwArgs || {});
        kwArgs[arg[2]] = argCode;
      } else {
        // positional argument
        args = (args || []);
        args.push(argCode);
      }
    });

    // put kwArgs in options dictionary at end of args
    if (kwArgs) {
      args = (args || []);
      args.push('Spacebars.kw(' + makeObjectLiteral(kwArgs) + ')');
    }

    return args;
  },

  codeGenBlock: function (content) {
    return SpacebarsCompiler.codeGen(content);
  },

  codeGenInclusionDataFunc: function (args) {
    var self = this;

    var dataFuncCode = null;

    if (! args.length) {
      // e.g. `{{#foo}}`
      return null;
    } else if (args[0].length === 3) {
      // keyword arguments only, e.g. `{{> point x=1 y=2}}`
      var dataProps = {};
      _.each(args, function (arg) {
        var argKey = arg[2];
        dataProps[argKey] = 'Spacebars.call(' + self.codeGenArgValue(arg) + ')';
      });
      dataFuncCode = makeObjectLiteral(dataProps);
    } else if (args[0][0] !== 'PATH') {
      // literal first argument, e.g. `{{> foo "blah"}}`
      //
      // tag validation has confirmed, in this case, that there is only
      // one argument (`args.length === 1`)
      dataFuncCode = self.codeGenArgValue(args[0]);
    } else if (args.length === 1) {
      // one argument, must be a PATH
      dataFuncCode = 'Spacebars.call(' + self.codeGenPath(args[0][1]) + ')';
    } else {
      // Multiple positional arguments; treat them as a nested
      // "data mustache"
      dataFuncCode = self.codeGenMustache(args[0][1], args.slice(1),
                                          'dataMustache');
    }

    return 'function () { return ' + dataFuncCode + '; }';
  }

});
<|endoftext|>


<commit_before>
var format = require('util').format
  , request = require('request')
  , async = require('async')

/**
 * The Trello class
 */
var Trello = module.exports = function(apiKey, authToken) {
  this.apiKey = apiKey;
  this.authToken = authToken;
}

/**
 * Get a board id from its url
 *
 * @param {String} boardUrl The board url
 * @return {String} The board id
 */
var getBoardIdFromUrl = function(boardUrl) {
  var urlParts = boardUrl.split('/'), part;

  while(urlParts.length) {
    part = urlParts.shift();
    if('b' === part && urlParts.length) {
      return urlParts[0];
    }
  }

  var msg = format('Could not get board id from url: "%s"', boardUrl);
  throw new Error(msg);
};

Trello.getBoardIdFromUrl = getBoardIdFromUrl;
Trello.prototype.getBoardIdFromUrl = getBoardIdFromUrl;

/**
 * Fetches trello lists for the board with the given id
 *
 * Callback will be passed an error if there is one and the array of lists.
 *
 * @param {String} boardId ID of the board to get lists for
 * @param {Function} cb The callback
 * @return {Function} The list getter function
 */
Trello.prototype.getBoardLists = function(boardId, cb) {
  var apiKey = this.apiKey
    , authToken = this.authToken
    , uri = format('https://api.trello.com/1/boards/%s/lists?key=%s&token=%s', boardId, apiKey, authToken);
  request(uri, function(error, resp, body) {
    if(error) { return cb(error); }
    var lists;
    try {
      lists = JSON.parse(body);
    } catch(error) {
      return cb(error);
    }
    return cb(null, lists);
  });
};

/**
 * Fetches trello cards for the given lists
 *
 * Note that cards are not separated by list.
 *
 * @param {Array} lists
 * @param {Function} cb
 */
Trello.prototype.getListsCards = function(lists, cb) {
  var apiKey = this.apiKey
    , authToken = this.authToken;
  async.map(
    lists,
    function(list, cb) {
      var listId = list.id
        , uri = format('https://api.trello.com/1/lists/%s/cards?key=%s&token=%s', listId, apiKey, authToken);
      request(uri, function(error, resp, body) {
        if(error) { return cb(error); }
        var cards;
        try {
          cards = JSON.parse(body);
        } catch(error) {
          return cb(error);
        }
        return cb(null, cards);
      });
    },
    function(err, cardsAndCards) {
      if(err) { return cb(err); }
      var allCards = [];
      cardsAndCards.forEach(function(cards) {
        Array.prototype.push.apply(allCards, cards);
      });
      cb(null, allCards);
    }
  );
};

<commit_msg>feature: Add util to get all cards in a board<commit_after>
var format = require('util').format
  , request = require('request')
  , async = require('async')

/**
 * The Trello class
 */
var Trello = module.exports = function(apiKey, authToken) {
  this.apiKey = apiKey;
  this.authToken = authToken;
}

/**
 * Get a board id from its url
 *
 * If the passed string is already an ID it will be returned
 *
 * @param {String} boardUrl The board url
 * @return {String} The board id
 */
var getBoardIdFromUrl = function(boardUrl) {
  var urlParts = boardUrl.split('/'), part;

  if(1 === urlParts.length) {
    // Already an ID?
    return boardUrl;
  }

  while(urlParts.length) {
    part = urlParts.shift();
    if('b' === part && urlParts.length) {
      return urlParts[0];
    }
  }

  var msg = format('Could not get board id from url: "%s"', boardUrl);
  throw new Error(msg);
};

Trello.getBoardIdFromUrl = getBoardIdFromUrl;
Trello.prototype.getBoardIdFromUrl = getBoardIdFromUrl;

/**
 * Fetches trello lists for the board with the given id
 *
 * Callback will be passed an error if there is one and the array of lists.
 *
 * @param {String} boardId ID of the board to get lists for
 * @param {Function} cb The callback
 */
Trello.prototype.getBoardLists = function(boardId, cb) {
  boardId = getBoardIdFromUrl(boardId);
  var apiKey = this.apiKey
    , authToken = this.authToken
    , uri = format('https://api.trello.com/1/boards/%s/lists?key=%s&token=%s', boardId, apiKey, authToken);
  request(uri, function(error, resp, body) {
    if(error) { return cb(error); }
    var lists;
    try {
      lists = JSON.parse(body);
    } catch(error) {
      return cb(error);
    }
    return cb(null, lists);
  });
};

/**
 * Get a list of cards for a given board
 *
 * @param {String} boardId The id or url for a given board
 * @param {Function} cb The callback
 */
Trello.prototype.getBoardCards = function(boardId, cb) {
  boardId = getBoardIdFromUrl(boardId);
  var apiKey = this.apiKey
    , authToken = this.authToken
    , uri = format('https://api.trello.com/1/boards/%s/cards?key=%s&token=%s', boardId, apiKey, authToken);
  request(uri, function(error, resp, body) {
    if(error) { return cb(error); }
    var cards;
    try {
      cards = JSON.parse(body);
    } catch(error) {
      return cb(error);
    }
    return cb(null, cards);
  });
};

/**
 * Fetches trello cards for the given lists
 *
 * Note that cards are not separated by list.
 *
 * @param {Array} lists
 * @param {Function} cb
 */
Trello.prototype.getListsCards = function(lists, cb) {
  var apiKey = this.apiKey
    , authToken = this.authToken;
  async.map(
    lists,
    function(list, cb) {
      var listId = list.id
        , uri = format('https://api.trello.com/1/lists/%s/cards?key=%s&token=%s', listId, apiKey, authToken);
      request(uri, function(error, resp, body) {
        if(error) { return cb(error); }
        var cards;
        try {
          cards = JSON.parse(body);
        } catch(error) {
          return cb(error);
        }
        return cb(null, cards);
      });
    },
    function(err, cardsAndCards) {
      if(err) { return cb(err); }
      var allCards = [];
      cardsAndCards.forEach(function(cards) {
        Array.prototype.push.apply(allCards, cards);
      });
      cb(null, allCards);
    }
  );
};

<|endoftext|>


<commit_before>var Trello = require("node-trello"),
    crypto = require('crypto'),
    util = require('util'),
    ee = require('events');

function dataparser(req, cb) {
  var str = '';

  req
    .on('error', cb)
    .on('data', function(chunk) {
      str += chunk;
    }).on('end', function() {
      req.body = str;
      cb(null, str);
    });
}

var Trobot = module.exports = function() {
  var self = this;
  self.grabUserdata();
  self.trello = new Trello(this.data.key, this.data.token);

  ee.call(self);

  self.on('error', function(err, statusCode, res){
    console.error(err.message || err);
    res.statusCode = statusCode;
    res.end();
  });

  self.on('request', function(req, res){
    dataparser(req, function(err, str){
      if (err) {
        self.emit('error', err, 400, res);
        return
      }
      self.emit('log', 'dataparser ok');

      var flag = self.originIsTrello(req);
      if (!flag) {
        self.emit('error', 'origin is not trello', 401, res);
        return
      }
      self.emit('log', 'origin is trello');

      try {
        var data = JSON.parse(str),
            event = data.action.type,
            model = data.model.name || data.model.fullName;

        self.emit('log', event + ' on ' + model + ' called');
        self.emit(event, data, res);

      } catch(err) {
        self.emit('error', err, 400, res);
      }
    });
  });

  self.on('reply', function(cardId, answer, res){
    var url = "/1/cards/" + cardId + "/actions/comments",
    		payload = {text: answer};

    self.trello.post(url, payload, function(err, data) {
      if (err) {
        self.emit('error', err, 500, res);
        return
      }
      self.emit('log', 'reply ok');
      res.statusCode = 200;
      res.end();
    });
  });
}

util.inherits(Trobot, ee);

Trobot.prototype.grabUserdata = function() {
  this.data = {};

  ['key', 'token', 'secret', 'userid', 'username', 'webhookCallbackURLdefault']
    .forEach(function(key){
      this.data[key] = process.env[key.toUpperCase()];
    }, this);
}

Trobot.prototype.originIsTrello = function(request, secret, callbackURL) {
  var base64Digest = function (s) {
        return crypto.createHmac('sha1', secret).update(s).digest('base64');
      };
  secret = secret || this.data.secret;
  callbackURL = callbackURL || this.data.webhookCallbackURLdefault;

  var content = request.body + callbackURL;
  var doubleHash = base64Digest(base64Digest(content));
  var headerHash = base64Digest(request.headers['x-trello-webhook']);
  return doubleHash == headerHash;
}
<commit_msg>add user to logged event<commit_after>var Trello = require("node-trello"),
    crypto = require('crypto'),
    util = require('util'),
    ee = require('events');

function dataparser(req, cb) {
  var str = '';

  req
    .on('error', cb)
    .on('data', function(chunk) {
      str += chunk;
    }).on('end', function() {
      req.body = str;
      cb(null, str);
    });
}

var Trobot = module.exports = function() {
  var self = this;
  self.grabUserdata();
  self.trello = new Trello(this.data.key, this.data.token);

  ee.call(self);

  self.on('error', function(err, statusCode, res){
    console.error(err.message || err);
    res.statusCode = statusCode;
    res.end();
  });

  self.on('request', function(req, res){
    dataparser(req, function(err, str){
      if (err) {
        self.emit('error', err, 400, res);
        return
      }
      self.emit('log', 'dataparser ok');

      var flag = self.originIsTrello(req);
      if (!flag) {
        self.emit('error', 'origin is not trello', 401, res);
        return
      }
      self.emit('log', 'origin is trello');

      try {
        var data = JSON.parse(str),
            event = data.action.type,
            model = data.model.name || data.model.fullName,
            user = data.action.memberCreator.fullName;

        self.emit('log', event + ' on ' + model + ' called by ' + user);
        self.emit(event, data, res);

      } catch(err) {
        self.emit('error', err, 400, res);
      }
    });
  });

  self.on('reply', function(cardId, answer, res){
    var url = "/1/cards/" + cardId + "/actions/comments",
    		payload = {text: answer};

    self.trello.post(url, payload, function(err, data) {
      if (err) {
        self.emit('error', err, 500, res);
        return
      }
      self.emit('log', 'reply ok');
      res.statusCode = 200;
      res.end();
    });
  });
}

util.inherits(Trobot, ee);

Trobot.prototype.grabUserdata = function() {
  this.data = {};

  ['key', 'token', 'secret', 'userid', 'username', 'webhookCallbackURLdefault']
    .forEach(function(key){
      this.data[key] = process.env[key.toUpperCase()];
    }, this);
}

Trobot.prototype.originIsTrello = function(request, secret, callbackURL) {
  var base64Digest = function (s) {
        return crypto.createHmac('sha1', secret).update(s).digest('base64');
      };
  secret = secret || this.data.secret;
  callbackURL = callbackURL || this.data.webhookCallbackURLdefault;

  var content = request.body + callbackURL;
  var doubleHash = base64Digest(base64Digest(content));
  var headerHash = base64Digest(request.headers['x-trello-webhook']);
  return doubleHash == headerHash;
}
<|endoftext|>


<commit_before>const accountSid = process.env.TWILIO_SID;
const authToken = process.env.TWILIO_AUTHTOKEN;

const client = require('twilio')(accountSid, authToken);

function sendTestMessage() {
  client.messages.create({
    body: 'Testy besty',
    to: process.env.TWILIO_DEST_NUMBER,
    from: process.env.TWILIO_NUMBER_1
  }, (error, message) => {
    console.log(error, message);
  });
}

module.exports = {
  sendTestMessage: sendTestMessage
};
<commit_msg>Twilio test<commit_after>const accountSid = process.env.TWILIO_SID;
const authToken = process.env.TWILIO_AUTHTOKEN;

const fromNumber = process.env.TWILIO_NUMBER_1;
const toNumber = process.env.TWILIO_DEST_NUMBER;

const client = require('twilio')(accountSid, authToken);

function sendTestMessage() {
  client.messages.create({
    body: 'Test',
    from: fromNumber,
    to: toNumber
  }, (error, message) => {
    console.log(error, message);
  });
}

module.exports = {
  sendTestMessage: sendTestMessage
};
<|endoftext|>


<commit_before>var crypto = require('crypto');
var xml2js = require('xml2js');
var ejs = require('ejs');
var BufferHelper = require('bufferhelper');
var Session = require('./session');
var List = require('./list');

var checkSignature = function (query, token) {
  var signature = query.signature;
  var timestamp = query.timestamp;
  var nonce = query.nonce;

  var shasum = crypto.createHash('sha1');
  var arr = [token, timestamp, nonce].sort();
  shasum.update(arr.join(''));

  return shasum.digest('hex') === signature;
};

var tpl = ['<xml>',
    '<ToUserName><![CDATA[<%-toUsername%>]]></ToUserName>',
    '<FromUserName><![CDATA[<%-fromUsername%>]]></FromUserName>',
    '<CreateTime><%=createTime%></CreateTime>',
    '<MsgType><![CDATA[<%=msgType%>]]></MsgType>',
  '<% if (msgType === "news") { %>',
    '<ArticleCount><%=content.length%></ArticleCount>',
    '<Articles>',
    '<% content.forEach(function(item){ %>',
      '<item>',
        '<Title><![CDATA[<%-item.title%>]]></Title>',
        '<Description><![CDATA[<%-item.description%>]]></Description>',
        '<PicUrl><![CDATA[<%-item.picUrl || item.picurl || item.pic %>]]></PicUrl>',
        '<Url><![CDATA[<%-item.url%>]]></Url>',
      '</item>',
    '<% }); %>',
    '</Articles>',
  '<% } else if (msgType === "music") { %>',
    '<Music>',
      '<Title><![CDATA[<%-content.title%>]]></Title>',
      '<Description><![CDATA[<%-content.description%>]]></Description>',
      '<MusicUrl><![CDATA[<%-content.musicUrl || content.url %>]]></MusicUrl>',
      '<HQMusicUrl><![CDATA[<%-content.hqMusicUrl || content.hqUrl %>]]></HQMusicUrl>',
    '</Music>',
  '<% } else { %>',
    '<Content><![CDATA[<%-content%>]]></Content>',
  '<% } %>',
    '<FuncFlag><%=funcFlag%></FuncFlag>',
  '</xml>'].join('');

var compiled = ejs.compile(tpl);

var getMessage = function (stream, callback) {
  var buf = new BufferHelper();
  buf.load(stream, function (err, buf) {
    if (err) {
      return callback(err);
    }
    var xml = buf.toString('utf-8');
    xml2js.parseString(xml, {trim: true}, callback);
  });
};

var isEmpty = function (thing) {
  return typeof thing === "object" && (thing != null) && Object.keys(thing).length === 0;
};

var formatMessage = function (result) {
  var message = {};
  for (var key in result.xml) {
    var val = result.xml[key][0];
    message[key] = (isEmpty(val) ? '' : val).trim();
  }
  return message;
};

var respond = function (handler) {
  return function (req, res, next) {
    getMessage(req, function (err, result) {
      if (err) {
        err.name = 'BadMessage' + err.name;
        return next(err);
      }
      var message = formatMessage(result);
      var callback = handler.getHandler(message.MsgType);
      req.weixin = message;
      res.reply = function (content, funcFlag) {
        var info = {};
        var type = 'text';
        info.content = content || '';
        if (Array.isArray(content)) {
          type = 'news';
        } else if (typeof content === 'object') {
          if (content.type === 'text') {
            type = 'text';
            info.content = content.content;
          } else {
            type = 'music';
          }
        } else {
          type = 'text';
        }
        info.msgType = type;
        info.createTime = new Date().getTime();
        info.funcFlag = funcFlag ? 1 : 0;
        info.toUsername = message.FromUserName;
        info.fromUsername = message.ToUserName;
        res.writeHead(200);
        res.end(compiled(info));
      };

      var done = function () {
        // 如果session中有_wait标记
        if (message.MsgType === 'text' && req.wxsession && req.wxsession._wait) {
          var list = List.get(req.wxsession._wait);
          var handle = list.get(message.Content);
          var wrapper = function (message) {
            return handler.handle ? function(req, res) {
              res.reply(message);
            } : function (info, req, res) {
              res.reply(message);
            };
          };

          // 如果回复命中规则，则用预置的方法回复
          if (handle) {
            callback = typeof handle === 'string' ? wrapper(handle) : handle;
          }
        }

        // 兼容旧API
        if (handler.handle) {
          callback(req, res, next);
        } else {
          callback(message, req, res, next);
        }
      };

      if (req.sessionStore) {
        var storage = req.sessionStore;
        var _end = res.end;
        var userOpenid = message.FromUserName;
	    var serviceOpenid = message.ToUserName;
	    var _str = serviceOpenid + ":" + userOpenid;
	    var _hash = crypto.createHash("md5");
	    _hash.update(_str);
	    var _hashmsg = _hash.digest('hex');
	    var hashid = _hashmsg.toString();
        res.end = function () {
          _end.apply(res, arguments);
          if (req.wxsession) {
            req.wxsession.save();
          }
        };
        // 等待列表
        res.wait = function (name, callback) {
          var list = List.get(name);
          if (list) {
            req.wxsession._wait = name;
            res.reply(list.description);
          } else {
            var err = new Error('Undefined list: ' + name);
            err.name = 'UndefinedListError';
            res.writeHead(500);
            res.end(err.name);
            callback && callback(err);
          }
        };

        // 清除等待列表
        res.nowait = function () {
          delete req.wxsession._wait;
          res.reply.apply(res, arguments);
        };

        storage.get(hashid, function (err, session) {
          if (!session) {
            req.wxsession = new Session(hashid, req);
            req.wxsession.cookie = req.session.cookie;
          } else {
            req.wxsession = new Session(hashid, req, session);
          }
          done();
        });
      } else {
        done();
      }
    });
  };
};

var Handler = function (token, handle) {
  this.token = token;
  this.handlers = {};
  this.handle = handle;
};

Handler.prototype.setHandler = function (type, fn) {
  this.handlers[type] = fn;
  return this;
};

Handler.prototype.getHandler = function (type) {
  return this.handle || this.handlers[type] || function (info, req, res, next) {
    next();
  };
};
['text', 'image', 'location', 'voice', 'link', 'event'].forEach(function (method) {
  Handler.prototype[method] = function (fn) {
    return this.setHandler(method, fn);
  };
});

Handler.prototype.middlewarify = function () {
  var token = this.token;
  var _respond = respond(this);
  return function (req, res, next) {
    // 动态token，在前置中间件中设置该值req.wechat_token，优先选用
    if (!checkSignature(req.query, req.wechat_token || token)) {
      res.writeHead(401);
      res.end('Invalid signature');
      return;
    }
    var method = req.method;
    if (method === 'GET') {
      res.writeHead(200);
      res.end(req.query.echostr);
    } else if (method === 'POST') {
      _respond(req, res, next);
    } else {
      res.writeHead(501);
      res.end('Not Implemented');
    }
  };
};

/**
 * Examples:
 * ```
 * wechat(token, function (req, res, next) {});
 * wechat(token, wechat.text(function (message, req, res, next) {
 * }).location(function (message, req, res, next) {
 * }));
 * wechat(token)
 *  .text(function (message, req, res, next) {})
 *  .location(function (message, req, res, next) {})
 *  .middleware();
 * ```
 * @param {String} token 在微信平台填写的口令
 * @param {Function} handle 生成的回调函数，参见示例
 */
var middleware = function (token, handle) {
  if (arguments.length === 1) {
    return new Handler(token);
  }

  if (arguments.length === 2) {
    if (handle instanceof Handler) {
      handle.token = token;
      return handle.middlewarify();
    } else {
      return new Handler(token, handle).middlewarify();
    }
  }
};

/**
 * 文字推送处理
 * @param {Function} fn 处理文字推送的回调函数，接受参数为(text, req, res, next)。
 */

/**
 * 图片推送处理
 * @param {Function} fn 处理图片推送的回调函数，接受参数为(image, req, res, next)。
 */

/**
 * 位置推送处理
 * @param {Function} fn 处理位置推送的回调函数，接受参数为(location, req, res, next)。
 */

/**
 * 声音推送处理
 * @param {Function} fn 处理声音推送的回调函数，接受参数为(voice, req, res, next)。
 */

/**
 * 链接推送处理
 * @param {Function} fn 处理链接推送的回调函数，接受参数为(link, req, res, next)。
 */

/**
 * 事件推送处理
 * @param {Function} fn 处理事件推送的回调函数，接受参数为(event, req, res, next)。
 */
['text', 'image', 'location', 'voice', 'link', 'event'].forEach(function (method) {
  middleware[method] = function (fn) {
    return (new Handler())[method](fn);
  };
});

module.exports = middleware;
module.exports.toXML = compiled;
<commit_msg>某人觉得md5运算的块要单独提出来，后来又提到其实不做md5也一样，另外的某人一琢磨也对，就提交了<commit_after>var crypto = require('crypto');
var xml2js = require('xml2js');
var ejs = require('ejs');
var BufferHelper = require('bufferhelper');
var Session = require('./session');
var List = require('./list');

var checkSignature = function (query, token) {
  var signature = query.signature;
  var timestamp = query.timestamp;
  var nonce = query.nonce;

  var shasum = crypto.createHash('sha1');
  var arr = [token, timestamp, nonce].sort();
  shasum.update(arr.join(''));

  return shasum.digest('hex') === signature;
};

var tpl = ['<xml>',
    '<ToUserName><![CDATA[<%-toUsername%>]]></ToUserName>',
    '<FromUserName><![CDATA[<%-fromUsername%>]]></FromUserName>',
    '<CreateTime><%=createTime%></CreateTime>',
    '<MsgType><![CDATA[<%=msgType%>]]></MsgType>',
  '<% if (msgType === "news") { %>',
    '<ArticleCount><%=content.length%></ArticleCount>',
    '<Articles>',
    '<% content.forEach(function(item){ %>',
      '<item>',
        '<Title><![CDATA[<%-item.title%>]]></Title>',
        '<Description><![CDATA[<%-item.description%>]]></Description>',
        '<PicUrl><![CDATA[<%-item.picUrl || item.picurl || item.pic %>]]></PicUrl>',
        '<Url><![CDATA[<%-item.url%>]]></Url>',
      '</item>',
    '<% }); %>',
    '</Articles>',
  '<% } else if (msgType === "music") { %>',
    '<Music>',
      '<Title><![CDATA[<%-content.title%>]]></Title>',
      '<Description><![CDATA[<%-content.description%>]]></Description>',
      '<MusicUrl><![CDATA[<%-content.musicUrl || content.url %>]]></MusicUrl>',
      '<HQMusicUrl><![CDATA[<%-content.hqMusicUrl || content.hqUrl %>]]></HQMusicUrl>',
    '</Music>',
  '<% } else { %>',
    '<Content><![CDATA[<%-content%>]]></Content>',
  '<% } %>',
    '<FuncFlag><%=funcFlag%></FuncFlag>',
  '</xml>'].join('');

var compiled = ejs.compile(tpl);

var getMessage = function (stream, callback) {
  var buf = new BufferHelper();
  buf.load(stream, function (err, buf) {
    if (err) {
      return callback(err);
    }
    var xml = buf.toString('utf-8');
    xml2js.parseString(xml, {trim: true}, callback);
  });
};

var isEmpty = function (thing) {
  return typeof thing === "object" && (thing != null) && Object.keys(thing).length === 0;
};

var formatMessage = function (result) {
  var message = {};
  for (var key in result.xml) {
    var val = result.xml[key][0];
    message[key] = (isEmpty(val) ? '' : val).trim();
  }
  return message;
};

var respond = function (handler) {
  return function (req, res, next) {
    getMessage(req, function (err, result) {
      if (err) {
        err.name = 'BadMessage' + err.name;
        return next(err);
      }
      var message = formatMessage(result);
      var callback = handler.getHandler(message.MsgType);
      req.weixin = message;
      res.reply = function (content, funcFlag) {
        var info = {};
        var type = 'text';
        info.content = content || '';
        if (Array.isArray(content)) {
          type = 'news';
        } else if (typeof content === 'object') {
          if (content.type === 'text') {
            type = 'text';
            info.content = content.content;
          } else {
            type = 'music';
          }
        } else {
          type = 'text';
        }
        info.msgType = type;
        info.createTime = new Date().getTime();
        info.funcFlag = funcFlag ? 1 : 0;
        info.toUsername = message.FromUserName;
        info.fromUsername = message.ToUserName;
        res.writeHead(200);
        res.end(compiled(info));
      };

      var done = function () {
        // 如果session中有_wait标记
        if (message.MsgType === 'text' && req.wxsession && req.wxsession._wait) {
          var list = List.get(req.wxsession._wait);
          var handle = list.get(message.Content);
          var wrapper = function (message) {
            return handler.handle ? function(req, res) {
              res.reply(message);
            } : function (info, req, res) {
              res.reply(message);
            };
          };

          // 如果回复命中规则，则用预置的方法回复
          if (handle) {
            callback = typeof handle === 'string' ? wrapper(handle) : handle;
          }
        }

        // 兼容旧API
        if (handler.handle) {
          callback(req, res, next);
        } else {
          callback(message, req, res, next);
        }
      };

      if (req.sessionStore) {
        var storage = req.sessionStore;
        var _end = res.end;
        var userOpenid = message.FromUserName;
        var SPOpenid = message.ToUserName;
        var mixId = SPOpenid + ":" + userOpenid;
        res.end = function () {
          _end.apply(res, arguments);
          if (req.wxsession) {
            req.wxsession.save();
          }
        };
        // 等待列表
        res.wait = function (name, callback) {
          var list = List.get(name);
          if (list) {
            req.wxsession._wait = name;
            res.reply(list.description);
          } else {
            var err = new Error('Undefined list: ' + name);
            err.name = 'UndefinedListError';
            res.writeHead(500);
            res.end(err.name);
            callback && callback(err);
          }
        };

        // 清除等待列表
        res.nowait = function () {
          delete req.wxsession._wait;
          res.reply.apply(res, arguments);
        };

        storage.get(mixId, function (err, session) {
          if (!session) {
            req.wxsession = new Session(mixId, req);
            req.wxsession.cookie = req.session.cookie;
          } else {
            req.wxsession = new Session(mixId, req, session);
          }
          done();
        });
      } else {
        done();
      }
    });
  };
};

var Handler = function (token, handle) {
  this.token = token;
  this.handlers = {};
  this.handle = handle;
};

Handler.prototype.setHandler = function (type, fn) {
  this.handlers[type] = fn;
  return this;
};

Handler.prototype.getHandler = function (type) {
  return this.handle || this.handlers[type] || function (info, req, res, next) {
    next();
  };
};
['text', 'image', 'location', 'voice', 'link', 'event'].forEach(function (method) {
  Handler.prototype[method] = function (fn) {
    return this.setHandler(method, fn);
  };
});

Handler.prototype.middlewarify = function () {
  var token = this.token;
  var _respond = respond(this);
  return function (req, res, next) {
    // 动态token，在前置中间件中设置该值req.wechat_token，优先选用
    if (!checkSignature(req.query, req.wechat_token || token)) {
      res.writeHead(401);
      res.end('Invalid signature');
      return;
    }
    var method = req.method;
    if (method === 'GET') {
      res.writeHead(200);
      res.end(req.query.echostr);
    } else if (method === 'POST') {
      _respond(req, res, next);
    } else {
      res.writeHead(501);
      res.end('Not Implemented');
    }
  };
};

/**
 * Examples:
 * ```
 * wechat(token, function (req, res, next) {});
 * wechat(token, wechat.text(function (message, req, res, next) {
 * }).location(function (message, req, res, next) {
 * }));
 * wechat(token)
 *  .text(function (message, req, res, next) {})
 *  .location(function (message, req, res, next) {})
 *  .middleware();
 * ```
 * @param {String} token 在微信平台填写的口令
 * @param {Function} handle 生成的回调函数，参见示例
 */
var middleware = function (token, handle) {
  if (arguments.length === 1) {
    return new Handler(token);
  }

  if (arguments.length === 2) {
    if (handle instanceof Handler) {
      handle.token = token;
      return handle.middlewarify();
    } else {
      return new Handler(token, handle).middlewarify();
    }
  }
};

/**
 * 文字推送处理
 * @param {Function} fn 处理文字推送的回调函数，接受参数为(text, req, res, next)。
 */

/**
 * 图片推送处理
 * @param {Function} fn 处理图片推送的回调函数，接受参数为(image, req, res, next)。
 */

/**
 * 位置推送处理
 * @param {Function} fn 处理位置推送的回调函数，接受参数为(location, req, res, next)。
 */

/**
 * 声音推送处理
 * @param {Function} fn 处理声音推送的回调函数，接受参数为(voice, req, res, next)。
 */

/**
 * 链接推送处理
 * @param {Function} fn 处理链接推送的回调函数，接受参数为(link, req, res, next)。
 */

/**
 * 事件推送处理
 * @param {Function} fn 处理事件推送的回调函数，接受参数为(event, req, res, next)。
 */
['text', 'image', 'location', 'voice', 'link', 'event'].forEach(function (method) {
  middleware[method] = function (fn) {
    return (new Handler())[method](fn);
  };
});

module.exports = middleware;
module.exports.toXML = compiled;
<|endoftext|>


<commit_before>const assert = require("chai").assert;
const accountsInfo = require("../lib/mnemonics/mnemonic");
const Config = require('truffle-config');
const defaultUserConfig = Config.getUserConfig();

describe("mnemonic", function() {
  describe("#getOrGenerateMnemonic", function(){
    it("checks user-level configuration for mnemonic and creates one if one is not present", function() {
      let mnemonic = accountsInfo.getOrGenerateMnemonic();
      assert.exists(mnemonic);
      assert.isString(mnemonic);
      assert.equal(mnemonic, defaultUserConfig.get('mnemonic'));
    });
  });
  describe("#getAccountsInfo", function(){
    it("returns public keys, private keys, and mnemonic for default user account", function() {
      let accounts = accountsInfo.getAccountsInfo();
      assert.exists(accounts);
      assert.isObject(accounts);
      assert.isArray(accounts.accounts);
      assert.isArray(accounts.privateKeys);
      assert.isString(accounts.mnemonic);
    });
  });
});
<commit_msg>updated test<commit_after>const assert = require("chai").assert;
const accountsInfo = require("../lib/mnemonics/mnemonic");
const Config = require('truffle-config');
const defaultUserConfig = Config.getUserConfig();

describe("mnemonic", function() {
  describe("#getOrGenerateMnemonic", function(){
    it("checks user-level configuration for mnemonic and creates one if one is not present", function() {
      let mnemonic = accountsInfo.getOrGenerateMnemonic();
      assert.exists(mnemonic);
      assert.isString(mnemonic);
      assert.equal(mnemonic, defaultUserConfig.get('mnemonic'));
    });
  });
  describe("#getAccountsInfo", function(){
    it("returns public keys, private keys, and mnemonic for default user account", function() {
      let defaultNumAddresses = 10;
      let accounts = accountsInfo.getAccountsInfo(defaultNumAddresses);
      assert.exists(accounts);
      assert.isObject(accounts);
      assert.isArray(accounts.accounts);
      assert.lengthOf(accounts.accounts, defaultNumAddresses);
      assert.isArray(accounts.privateKeys);
      assert.isString(accounts.mnemonic);
    });
  });
});
<|endoftext|>


<commit_before>import { Promise } from 'rsvp';
import { futureTick } from './-utils';

const TIMEOUTS = [0, 1, 2, 5, 7];
const MAX_TIMEOUT = 10;

/**
  Wait for the provided callback to return a truthy value.

  This does not leverage `settled()`, and as such can be used to manage async
  while _not_ settled (e.g. "loading" or "pending" states).

  @public
  @param {Function} callback the callback to use for testing when waiting should stop
  @param {Object} [options] options used to override defaults
  @param {number} [options.timeout=1000] the maximum amount of time to wait
  @returns {Promise} resolves with the callback value when it returns a truthy value
*/
export default function waitUntil(callback, options = {}) {
  let timeout = 'timeout' in options ? options.timeout : 1000;
  let timeoutMessage = 'timeoutMessage' in options ? options.timeoutMessage : 'waitUntil timed out';

  // creating this error eagerly so it has the proper invocation stack
  let waitUntilTimedOut = new Error(timeoutMessage);

  return new Promise(function(resolve, reject) {
    let time = 0;

    // eslint-disable-next-line require-jsdoc
    function scheduleCheck(timeoutsIndex) {
      let interval = TIMEOUTS[timeoutsIndex];
      if (interval === undefined) {
        interval = MAX_TIMEOUT;
      }

      futureTick(function() {
        time += interval;

        let value;
        try {
          value = callback();
        } catch (error) {
          reject(error);
        }

        if (value) {
          resolve(value);
        } else if (time < timeout) {
          scheduleCheck(timeoutsIndex + 1);
        } else {
          reject(waitUntilTimedOut);
        }
      }, interval);
    }

    scheduleCheck(0);
  });
}
<commit_msg>add description of waitUntil timeoutMessage parameter<commit_after>import { Promise } from 'rsvp';
import { futureTick } from './-utils';

const TIMEOUTS = [0, 1, 2, 5, 7];
const MAX_TIMEOUT = 10;

/**
  Wait for the provided callback to return a truthy value.

  This does not leverage `settled()`, and as such can be used to manage async
  while _not_ settled (e.g. "loading" or "pending" states).

  @public
  @param {Function} callback the callback to use for testing when waiting should stop
  @param {Object} [options] options used to override defaults
  @param {number} [options.timeout=1000] the maximum amount of time to wait
  @param {string} [options.timeoutMessage='waitUntil timed out'] the message to use in the reject on timeout
  @returns {Promise} resolves with the callback value when it returns a truthy value
*/
export default function waitUntil(callback, options = {}) {
  let timeout = 'timeout' in options ? options.timeout : 1000;
  let timeoutMessage = 'timeoutMessage' in options ? options.timeoutMessage : 'waitUntil timed out';

  // creating this error eagerly so it has the proper invocation stack
  let waitUntilTimedOut = new Error(timeoutMessage);

  return new Promise(function(resolve, reject) {
    let time = 0;

    // eslint-disable-next-line require-jsdoc
    function scheduleCheck(timeoutsIndex) {
      let interval = TIMEOUTS[timeoutsIndex];
      if (interval === undefined) {
        interval = MAX_TIMEOUT;
      }

      futureTick(function() {
        time += interval;

        let value;
        try {
          value = callback();
        } catch (error) {
          reject(error);
        }

        if (value) {
          resolve(value);
        } else if (time < timeout) {
          scheduleCheck(timeoutsIndex + 1);
        } else {
          reject(waitUntilTimedOut);
        }
      }, interval);
    }

    scheduleCheck(0);
  });
}
<|endoftext|>


<commit_before>const logger = require('winston');
const stringify = require('json-stringify-pretty-compact');
const handlebars = require('handlebars');
const versionsHelper = require('./helpers/versions');
const packageJson = require('./helpers/package-json');
const npmApi = require('./api/npm');
const prWorker = require('./workers/pr');
const branchWorker = require('./workers/branch');

let config;

module.exports = {
  processPackageFile,
  findUpgrades,
  processUpgrades,
  updateBranch,
  assignDepConfigs,
  getDepTypeConfig,
};

// This function manages the queue per-package file
async function processPackageFile(repoName, packageFile, packageConfig) {
  // Initialize globals
  config = Object.assign({}, packageConfig);
  config.packageFile = packageFile;

  logger.info(`Processing ${repoName} ${packageFile}`);

  const packageContent = await config.api.getFileJson(packageFile);
  // Check for renovate config inside the package.json
  if (packageContent.renovate) {
    logger.debug(`package.json>renovate config: ${stringify(packageContent.renovate)}`);
    Object.assign(config, packageContent.renovate, { repoConfigured: true });
  }
  // Now check if config is disabled
  if (config.enabled === false) {
    logger.info('Config is disabled. Skipping');
    return [];
  }

  const depTypes = config.depTypes.map(depType => {
    if (typeof depType === 'string') {
      return depType;
    }
    return depType.depType;
  });

  // Extract all dependencies from the package.json
  let dependencies = await packageJson.extractDependencies(
    packageContent,
    depTypes
  );
  // Filter out ignored dependencies
  dependencies = dependencies.filter(
    dependency => config.ignoreDeps.indexOf(dependency.depName) === -1
  );
  dependencies = assignDepConfigs(config, dependencies);
  // Find all upgrades for remaining dependencies
  const upgrades = await findUpgrades(dependencies);
  // Process all upgrades sequentially
  if (config.maintainYarnLock) {
    const upgrade = Object.assign({}, config, {
      upgradeType: 'maintainYarnLock',
    });
    upgrade.upgradeType = 'maintainYarnLock';
    upgrade.branchName = upgrade.yarnMaintenanceBranchName;
    upgrade.prTitle = upgrade.yarnMaintenancePrTitle;
    upgrade.prBody = upgrade.yarnMaintenancePrBody;
    upgrades.push(upgrade);
  }
  return upgrades;
}

// Add custom config for each dep
function assignDepConfigs(inputConfig, deps) {
  return deps.map(dep => {
    const returnDep = Object.assign({}, dep);
    returnDep.config = Object.assign(
      {},
      inputConfig,
      getDepTypeConfig(inputConfig.depTypes, dep.depType)
    );
    let packageRuleApplied = false;
    if (returnDep.config.packages) {
      // Loop through list looking for match
      // Exit after first match
      returnDep.config.packages.forEach(packageConfig => {
        if (!packageRuleApplied) {
          const pattern =
            packageConfig.packagePattern || `^${packageConfig.packageName}$`;
          const packageRegex = new RegExp(pattern);
          if (dep.depName.match(packageRegex)) {
            packageRuleApplied = true;
            Object.assign(returnDep.config, packageConfig);
            delete returnDep.config.packageName;
            delete returnDep.config.packagePattern;
          }
        }
      });
    }
    delete returnDep.config.depType;
    delete returnDep.config.depTypes;
    delete returnDep.config.enabled;
    delete returnDep.config.onboarding;
    delete returnDep.config.token;
    delete returnDep.config.packageFiles;
    delete returnDep.config.logLevel;
    delete returnDep.config.repoConfigured;
    delete returnDep.config.ignoreDeps;
    delete returnDep.config.packages;
    delete returnDep.config.maintainYarnLock;
    delete returnDep.config.yarnMaintenanceBranchName;
    delete returnDep.config.yarnMaintenanceCommitMessage;
    delete returnDep.config.yarnMaintenancePrTitle;
    delete returnDep.config.yarnMaintenancePrBody;
    return returnDep;
  });
}

function getDepTypeConfig(depTypes, depTypeName) {
  let depTypeConfig = {};
  if (depTypes) {
    depTypes.forEach(depType => {
      if (typeof depType !== 'string' && depType.depType === depTypeName) {
        depTypeConfig = depType;
      }
    });
  }
  return depTypeConfig;
}

async function findUpgrades(dependencies) {
  const allUpgrades = [];
  // findDepUpgrades can add more than one upgrade to allUpgrades
  async function findDepUpgrades(dep) {
    const npmDependency = await npmApi.getDependency(dep.depName);
    const upgrades = await versionsHelper.determineUpgrades(
      npmDependency,
      dep.currentVersion,
      dep.config
    );
    if (upgrades.length > 0) {
      logger.verbose(`${dep.depName}: Upgrades = ${JSON.stringify(upgrades)}`);
      upgrades.forEach(upgrade => {
        allUpgrades.push(Object.assign({}, dep, upgrade));
      });
    } else {
      logger.verbose(`${dep.depName}: No upgrades required`);
    }
  }
  const promiseArray = dependencies.map(dep => findDepUpgrades(dep));
  // Use Promise.all to execute npm queries in parallel
  await Promise.all(promiseArray);
  // Return the upgrade array once all Promises are complete
  return allUpgrades;
}

async function processUpgrades(upgrades) {
  if (upgrades.length) {
    logger.verbose('Processing upgrades');
  } else {
    logger.verbose('No upgrades to process');
  }
  logger.verbose(`All upgrades: ${JSON.stringify(upgrades)}`);
  const branchUpgrades = {};
  for (const upgrade of upgrades) {
    const flattened = Object.assign({}, upgrade.config, upgrade);
    delete flattened.config;
    if (flattened.upgradeType === 'pin') {
      flattened.isPin = true;
    } else if (flattened.upgradeType === 'major') {
      flattened.isMajor = true;
    } else if (flattened.upgradeType === 'minor') {
      flattened.isMinor = true;
    }
    // Check whether to use a group name
    if (flattened.groupName) {
      flattened.groupSlug =
        flattened.groupSlug ||
        flattened.groupName.toLowerCase().replace(/[^a-z0-9+]+/g, '-');
      flattened.branchName = flattened.groupBranchName;
      flattened.commitMessage = flattened.groupCommitMessage;
      flattened.prTitle = flattened.groupPrTitle;
      flattened.prBody = flattened.groupPrBody;
    }
    const branchName = handlebars.compile(flattened.branchName)(flattened);
    if (!branchUpgrades[branchName]) {
      branchUpgrades[branchName] = [];
    }
    branchUpgrades[branchName].push(flattened);
  }
  logger.verbose(`Branched upgrades: ${JSON.stringify(branchUpgrades)}`);
  for (const branch of Object.keys(branchUpgrades)) {
    await module.exports.updateBranch(branchUpgrades[branch]);
  }
}

async function updateBranch(upgrades) {
  // Use templates to generate strings
  const upgrade0 = upgrades[0];
  const branchName = handlebars.compile(upgrade0.branchName)(upgrade0);
  const prTitle = handlebars.compile(upgrade0.prTitle)(upgrade0);

  logger.verbose(`branchName '${branchName}' length is ${upgrades.length}`);

  try {
    if (
      upgrade0.upgradeType !== 'maintainYarnLock' &&
      upgrade0.groupName === null &&
      !upgrade0.recreateClosed &&
      (await upgrade0.api.checkForClosedPr(branchName, prTitle))
    ) {
      logger.verbose(`Skipping ${branchName} upgrade as matching closed PR already existed`);
      return;
    }
    const branchCreated = await branchWorker.ensureBranch(upgrades);
    if (branchCreated) {
      const pr = await prWorker.ensurePr(upgrade0);
      if (pr) {
        await prWorker.checkAutoMerge(pr, upgrade0);
      }
    }
  } catch (error) {
    logger.error(`Error updating branch ${branchName}: ${error}`);
    // Don't throw here - we don't want to stop the other renovations
  }
}
<commit_msg>Fix yarnMaintenanceCommitMessage use (#206)<commit_after>const logger = require('winston');
const stringify = require('json-stringify-pretty-compact');
const handlebars = require('handlebars');
const versionsHelper = require('./helpers/versions');
const packageJson = require('./helpers/package-json');
const npmApi = require('./api/npm');
const prWorker = require('./workers/pr');
const branchWorker = require('./workers/branch');

let config;

module.exports = {
  processPackageFile,
  findUpgrades,
  processUpgrades,
  updateBranch,
  assignDepConfigs,
  getDepTypeConfig,
};

// This function manages the queue per-package file
async function processPackageFile(repoName, packageFile, packageConfig) {
  // Initialize globals
  config = Object.assign({}, packageConfig);
  config.packageFile = packageFile;

  logger.info(`Processing ${repoName} ${packageFile}`);

  const packageContent = await config.api.getFileJson(packageFile);
  // Check for renovate config inside the package.json
  if (packageContent.renovate) {
    logger.debug(`package.json>renovate config: ${stringify(packageContent.renovate)}`);
    Object.assign(config, packageContent.renovate, { repoConfigured: true });
  }
  // Now check if config is disabled
  if (config.enabled === false) {
    logger.info('Config is disabled. Skipping');
    return [];
  }

  const depTypes = config.depTypes.map(depType => {
    if (typeof depType === 'string') {
      return depType;
    }
    return depType.depType;
  });

  // Extract all dependencies from the package.json
  let dependencies = await packageJson.extractDependencies(
    packageContent,
    depTypes
  );
  // Filter out ignored dependencies
  dependencies = dependencies.filter(
    dependency => config.ignoreDeps.indexOf(dependency.depName) === -1
  );
  dependencies = assignDepConfigs(config, dependencies);
  // Find all upgrades for remaining dependencies
  const upgrades = await findUpgrades(dependencies);
  // Process all upgrades sequentially
  if (config.maintainYarnLock) {
    const upgrade = Object.assign({}, config, {
      upgradeType: 'maintainYarnLock',
    });
    upgrade.upgradeType = 'maintainYarnLock';
    upgrade.commitMessage = upgrade.yarnMaintenanceCommitMessage;
    upgrade.branchName = upgrade.yarnMaintenanceBranchName;
    upgrade.prTitle = upgrade.yarnMaintenancePrTitle;
    upgrade.prBody = upgrade.yarnMaintenancePrBody;
    upgrades.push(upgrade);
  }
  return upgrades;
}

// Add custom config for each dep
function assignDepConfigs(inputConfig, deps) {
  return deps.map(dep => {
    const returnDep = Object.assign({}, dep);
    returnDep.config = Object.assign(
      {},
      inputConfig,
      getDepTypeConfig(inputConfig.depTypes, dep.depType)
    );
    let packageRuleApplied = false;
    if (returnDep.config.packages) {
      // Loop through list looking for match
      // Exit after first match
      returnDep.config.packages.forEach(packageConfig => {
        if (!packageRuleApplied) {
          const pattern =
            packageConfig.packagePattern || `^${packageConfig.packageName}$`;
          const packageRegex = new RegExp(pattern);
          if (dep.depName.match(packageRegex)) {
            packageRuleApplied = true;
            Object.assign(returnDep.config, packageConfig);
            delete returnDep.config.packageName;
            delete returnDep.config.packagePattern;
          }
        }
      });
    }
    delete returnDep.config.depType;
    delete returnDep.config.depTypes;
    delete returnDep.config.enabled;
    delete returnDep.config.onboarding;
    delete returnDep.config.token;
    delete returnDep.config.packageFiles;
    delete returnDep.config.logLevel;
    delete returnDep.config.repoConfigured;
    delete returnDep.config.ignoreDeps;
    delete returnDep.config.packages;
    delete returnDep.config.maintainYarnLock;
    delete returnDep.config.yarnMaintenanceBranchName;
    delete returnDep.config.yarnMaintenanceCommitMessage;
    delete returnDep.config.yarnMaintenancePrTitle;
    delete returnDep.config.yarnMaintenancePrBody;
    return returnDep;
  });
}

function getDepTypeConfig(depTypes, depTypeName) {
  let depTypeConfig = {};
  if (depTypes) {
    depTypes.forEach(depType => {
      if (typeof depType !== 'string' && depType.depType === depTypeName) {
        depTypeConfig = depType;
      }
    });
  }
  return depTypeConfig;
}

async function findUpgrades(dependencies) {
  const allUpgrades = [];
  // findDepUpgrades can add more than one upgrade to allUpgrades
  async function findDepUpgrades(dep) {
    const npmDependency = await npmApi.getDependency(dep.depName);
    const upgrades = await versionsHelper.determineUpgrades(
      npmDependency,
      dep.currentVersion,
      dep.config
    );
    if (upgrades.length > 0) {
      logger.verbose(`${dep.depName}: Upgrades = ${JSON.stringify(upgrades)}`);
      upgrades.forEach(upgrade => {
        allUpgrades.push(Object.assign({}, dep, upgrade));
      });
    } else {
      logger.verbose(`${dep.depName}: No upgrades required`);
    }
  }
  const promiseArray = dependencies.map(dep => findDepUpgrades(dep));
  // Use Promise.all to execute npm queries in parallel
  await Promise.all(promiseArray);
  // Return the upgrade array once all Promises are complete
  return allUpgrades;
}

async function processUpgrades(upgrades) {
  if (upgrades.length) {
    logger.verbose('Processing upgrades');
  } else {
    logger.verbose('No upgrades to process');
  }
  logger.verbose(`All upgrades: ${JSON.stringify(upgrades)}`);
  const branchUpgrades = {};
  for (const upgrade of upgrades) {
    const flattened = Object.assign({}, upgrade.config, upgrade);
    delete flattened.config;
    if (flattened.upgradeType === 'pin') {
      flattened.isPin = true;
    } else if (flattened.upgradeType === 'major') {
      flattened.isMajor = true;
    } else if (flattened.upgradeType === 'minor') {
      flattened.isMinor = true;
    }
    // Check whether to use a group name
    if (flattened.groupName) {
      flattened.groupSlug =
        flattened.groupSlug ||
        flattened.groupName.toLowerCase().replace(/[^a-z0-9+]+/g, '-');
      flattened.branchName = flattened.groupBranchName;
      flattened.commitMessage = flattened.groupCommitMessage;
      flattened.prTitle = flattened.groupPrTitle;
      flattened.prBody = flattened.groupPrBody;
    }
    const branchName = handlebars.compile(flattened.branchName)(flattened);
    if (!branchUpgrades[branchName]) {
      branchUpgrades[branchName] = [];
    }
    branchUpgrades[branchName].push(flattened);
  }
  logger.verbose(`Branched upgrades: ${JSON.stringify(branchUpgrades)}`);
  for (const branch of Object.keys(branchUpgrades)) {
    await module.exports.updateBranch(branchUpgrades[branch]);
  }
}

async function updateBranch(upgrades) {
  // Use templates to generate strings
  const upgrade0 = upgrades[0];
  const branchName = handlebars.compile(upgrade0.branchName)(upgrade0);
  const prTitle = handlebars.compile(upgrade0.prTitle)(upgrade0);

  logger.verbose(`branchName '${branchName}' length is ${upgrades.length}`);

  try {
    if (
      upgrade0.upgradeType !== 'maintainYarnLock' &&
      upgrade0.groupName === null &&
      !upgrade0.recreateClosed &&
      (await upgrade0.api.checkForClosedPr(branchName, prTitle))
    ) {
      logger.verbose(`Skipping ${branchName} upgrade as matching closed PR already existed`);
      return;
    }
    const branchCreated = await branchWorker.ensureBranch(upgrades);
    if (branchCreated) {
      const pr = await prWorker.ensurePr(upgrade0);
      if (pr) {
        await prWorker.checkAutoMerge(pr, upgrade0);
      }
    }
  } catch (error) {
    logger.error(`Error updating branch ${branchName}: ${error}`);
    // Don't throw here - we don't want to stop the other renovations
  }
}
<|endoftext|>