<commit_before>/**
 * marked - A markdown parser
 * Copyright (c) 2011, Christopher Jeffrey. (MIT Licensed)
 */

;(function() {

/**
 * Block-Level Grammar
 */

var block = {
  newline: /^\n+/,
  block: /^ {4,}[^\n]*(?:\n {4,}[^\n]*|\n)*(?=\n| *$)/,
  hr: /^( *[\-*_]){3,} *\n/,
  heading: /^ *(#{1,6}) *([^\0]+?) *#* *\n+/,
  lheading: /^([^\n]+)\n *(=|-){3,}/,
  blockquote: /^ *>[^\n]*(?:\n *>[^\n]*)*/,
  list: /^( *)([*+-]|\d+\.) [^\0]+?(?:\n{2,}(?! )|\s*$)(?!\1\2|\1\d+\.)/,
  html: /^ *(?:|<(\w+)[^\0]+?<\/\1>|<\w+[^>]*>) *(?:\n{2,}|\s*$)/,
  text: /^[^\n]+/
};

block.keys = [
  'newline',
  'block',
  'heading',
  'lheading',
  'hr',
  'blockquote',
  'list',
  'html',
  'text'
];

/**
 * Lexer
 */

block.lexer = function(str) {
  var tokens = []
    , links = {};

  str = str.replace(/\r\n|\r/g, '\n')
           .replace(/\t/g, '    ');

  str = str.replace(
    /^ {0,3}\[([^\]]+)\]: *([^ ]+)(?: +"([^\n]+)")? *$/gm,
    function(__, id, href, title) {
      links[id] = {
        href: href,
        title: title
      };
      return '';
    }
  );

  tokens.links = links;

  return block.token(str, tokens);
};

block.token = function(str, tokens) {
  str = str.replace(/^ +$/gm, '');

  var rules = block
    , keys = block.keys
    , len = keys.length
    , key
    , cap
    , loose;

  var scan = function() {
    if (!str) return;
    for (var i = 0; i < len; i++) {
      key = keys[i];
      if (cap = rules[key].exec(str)) {
        str = str.substring(cap[0].length);
        return true;
      }
    }
  };

  while (scan()) {
    switch (key) {
      case 'newline':
        if (cap[0].length > 1) {
          tokens.push({
            type: 'space'
          });
        }
        break;
      case 'hr':
        tokens.push({
          type: 'hr'
        });
        break;
      case 'lheading':
        tokens.push({
          type: 'heading',
          depth: cap[2] === '=' ? 1 : 2,
          text: cap[1]
        });
        break;
      case 'heading':
        tokens.push({
          type: 'heading',
          depth: cap[1].length,
          text: cap[2]
        });
        break;
      case 'block':
        cap = cap[0].replace(/^ {4}/gm, '');
        tokens.push({
          type: 'block',
          text: cap
        });
        break;
      case 'list':
        tokens.push({
          type: 'list_start',
          ordered: isFinite(cap[2])
        });
        loose = /\n *\n *(?:[*+-]|\d+\.)/.test(cap[0]);
        // get each top-level item
        cap = cap[0].match(
          /^( *)([*+-]|\d+\.)[^\n]*(?:\n(?!\1(?:\2|\d+\.))[^\n]*)*/gm
        );
        each(cap, function(item) {
          // remove the list items sigil
          // so its seen as the next token
          item = item.replace(/^ *([*+-]|\d+\.) */, '');
          // outdent whatever the
          // list item contains, hacky
          var space = /\n( +)/.exec(item);
          if (space) {
            space = new RegExp('^' + space[1], 'gm');
            item = item.replace(space, '');
          }
          tokens.push({
            type: loose
              ? 'loose_item_start'
              : 'list_item_start'
          });
          block.token(item, tokens);
          tokens.push({
            type: 'list_item_end'
          });
        });
        tokens.push({
          type: 'list_end'
        });
        break;
      case 'html':
      case 'text':
        tokens.push({
          type: key,
          text: cap[0]
        });
        break;
      case 'blockquote':
        tokens.push({
          type: 'blockquote_start'
        });
        cap = cap[0].replace(/^ *>/gm, '');
        block.token(cap, tokens);
        tokens.push({
          type: 'blockquote_end'
        });
        break;
    }
  }

  return tokens;
};

/**
 * Inline Processing
 */

var inline = {
  escape: /^\\([\\`*{}\[\]()#+\-.!_])/,
  autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
  tag: /^|^<\/?\w+[^>]*>/,
  link: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]\s*\(([^\)]*)\)/,
  reflink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]\s*\[([^\]]*)\]/,
  nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
  strong: /^__([^\0]+?)__(?!_)|^\*\*([^\0]+?)\*\*(?!\*)/,
  em: /^_([^_]+)_|^\*([^*]+)\*/,
  code: /^`([^`]+)`|^``([^\0]+?)``/,
  br: /^ {2,}\n(?!\s*$)/,
  text: /^/
};

inline.keys = [
  'escape',
  'autolink',
  'tag',
  'link',
  'reflink',
  'nolink',
  'strong',
  'em',
  'code',
  'br',
  'text'
];

// hacky, but performant
inline.text = (function() {
  var body = [];

  (function push(rule) {
    rule = inline[rule].source;
    body.push(rule.replace(/(^|[^\[])\^/g, '$1'));
    return push;
  })
  ('escape')
  ('tag')
  ('nolink')
  ('strong')
  ('em')
  ('code')
  ('br');

  return new
    RegExp('^[^\\0]+?(?=' + body.join('|') + '|$)');
})();

/**
 * Inline Lexer
 */

inline.lexer = function(str) {
  var out = ''
    , links = tokens.links
    , link
    , text
    , href;

  var rules = inline
    , keys = inline.keys
    , len = keys.length
    , key
    , cap;

  var scan = function() {
    if (!str) return;
    for (var i = 0; i < len; i++) {
      key = keys[i];
      if (cap = rules[key].exec(str)) {
        str = str.substring(cap[0].length);
        return true;
      }
    }
  };

  while (scan()) {
    switch (key) {
      case 'escape':
        out += cap[1];
        break;
      case 'tag':
        out += cap[0];
        break;
      case 'link':
      case 'reflink':
      case 'nolink':
        if (key !== 'link') {
          link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
          link = links[link];
          if (!link) {
            out += cap[0][0];
            str = cap[0].substring(1) + str;
            break;
          }
        } else {
          text = /^\s*<?([^\s]*?)>?(?:\s+"([^\n]+)")?\s*$/.exec(cap[2]);
          link = {
            href: text[1],
            title: text[2]
          };
        }
        if (cap[0][0] !== '!') {
          out += ''
            + inline.lexer(cap[1])
            + '';
        } else {
          out += '';
        }
        break;
      case 'autolink':
        if (cap[2] === '@') {
          text = cap[1][6] === ':'
            ? mangle(cap[1].substring(7))
            : mangle(cap[1]);
          href = mangle('mailto:') + text;
        } else {
          text = escape(cap[1]);
          href = text;
        }
        out += ''
          + text
          + '';
        break;
      case 'strong':
        out += ''
          + inline.lexer(cap[2] || cap[1])
          + '';
        break;
      case 'em':
        out += ''
          + inline.lexer(cap[2] || cap[1])
          + '';
        break;
      case 'code':
        out += ''
          + escape(cap[2] || cap[1])
          + '';
        break;
      case 'br':
        out += '
';
        break;
      case 'text':
        out += escape(cap[0]);
        break;
    }
  }

  return out;
};

/**
 * Parsing
 */

var tokens
  , token;

var next = function() {
  return token = tokens.pop();
};

var tok = function() {
  switch (token.type) {
    case 'space':
      return '';
    case 'hr':
      return '
';
    case 'heading':
      return '<h' + token.depth + '>'
        + inline.lexer(token.text)
        + '</h' + token.depth + '>';
    case 'block':
      return '
'
        + escape(token.text)
        + '
';
    case 'blockquote_start':
      var body = [];
      while (next().type !== 'blockquote_end') {
        body.push(tok());
      }
      return '
'
        + body.join('')
        + '
';
    case 'list_start':
      var type = token.ordered ? 'ol' : 'ul'
        , body = [];
      while (next().type !== 'list_end') {
        body.push(tok());
      }
      return '<' + type + '>'
        + body.join('')
        + '</' + type + '>';
    case 'list_item_start':
      var body = [];
      while (next().type !== 'list_item_end') {
        body.push(token.type === 'text'
          ? text()
          : tok());
      }
      return '
'
        + body.join(' ')
        + '
';
    case 'loose_item_start':
      var body = [];
      while (next().type !== 'list_item_end') {
        body.push(tok());
      }
      return '
'
        + body.join(' ')
        + '
';
    case 'html':
      return inline.lexer(token.text);
    case 'text':
      return '
' + text() + '
';
 }
};

var text = function() {
  var body = [ token.text ]
    , top;

  while ((top = tokens[tokens.length-1])
         && top.type === 'text') {
    body.push(next().text);
  }

  return inline.lexer(body.join('\n'));
};

var parse = function(src) {
  tokens = src.reverse();

  var out = [];
  while (next()) {
    out.push(tok());
  }

  tokens = null;
  token = null;

  return out.join(' ');
};

/**
 * Helpers
 */

var escape = function(html) {
  return html
    .replace(/&/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"')
    .replace(/'/g, ''');
};

var mangle = function(str) {
  var out = ''
    , ch
    , i = 0
    , l = str.length;

  for (; i < l; i++) {
    ch = str.charCodeAt(i);
    if (Math.random() > 0.5) {
      ch = 'x' + ch.toString(16);
    }
    out += '&#' + ch + ';';
  }

  return out;
};

var each = function(obj, func) {
  var i = 0, l = obj.length;
  for (; i < l; i++) func(obj[i]);
};

/**
 * Expose
 */

var marked = function(str) {
  return parse(block.lexer(str));
};

marked.parser = parse;
marked.lexer = block.lexer;

if (typeof module !== 'undefined') {
  module.exports = marked;
} else {
  this.marked = marked;
}

}).call(this);
<commit_msg>unroll loops for performance<commit_after>/**
 * marked - A markdown parser
 * Copyright (c) 2011, Christopher Jeffrey. (MIT Licensed)
 */

;(function() {

/**
 * Block-Level Grammar
 */

var block = {
  newline: /^\n+/,
  block: /^ {4,}[^\n]*(?:\n {4,}[^\n]*|\n)*(?=\n| *$)/,
  hr: /^( *[\-*_]){3,} *\n/,
  heading: /^ *(#{1,6}) *([^\0]+?) *#* *\n+/,
  lheading: /^([^\n]+)\n *(=|-){3,}/,
  blockquote: /^ *>[^\n]*(?:\n *>[^\n]*)*/,
  list: /^( *)([*+-]|\d+\.) [^\0]+?(?:\n{2,}(?! )|\s*$)(?!\1\2|\1\d+\.)/,
  html: /^ *(?:|<(\w+)[^\0]+?<\/\1>|<\w+[^>]*>) *(?:\n{2,}|\s*$)/,
  text: /^[^\n]+/
};

/**
 * Lexer
 */

block.lexer = function(str) {
  var tokens = []
    , links = {};

  str = str.replace(/\r\n|\r/g, '\n')
           .replace(/\t/g, '    ');

  str = str.replace(
    /^ {0,3}\[([^\]]+)\]: *([^ ]+)(?: +"([^\n]+)")? *$/gm,
    function(__, id, href, title) {
      links[id] = {
        href: href,
        title: title
      };
      return '';
    }
  );

  tokens.links = links;

  return block.token(str, tokens);
};

block.token = function(str, tokens) {
  var str = str.replace(/^ +$/gm, '')
    , loose
    , cap;

  while (str) {
    if (cap = block.newline.exec(str)) {
      str = str.substring(cap[0].length);
      if (cap[0].length > 1) {
        tokens.push({
          type: 'space'
        });
      }
      continue;
    }

    if (cap = block.block.exec(str)) {
      str = str.substring(cap[0].length);
      cap = cap[0].replace(/^ {4}/gm, '');
      tokens.push({
        type: 'block',
        text: cap
      });
      continue;
    }

    if (cap = block.heading.exec(str)) {
      str = str.substring(cap[0].length);
      tokens.push({
        type: 'heading',
        depth: cap[1].length,
        text: cap[2]
      });
      continue;
    }

    if (cap = block.lheading.exec(str)) {
      str = str.substring(cap[0].length);
      tokens.push({
        type: 'heading',
        depth: cap[2] === '=' ? 1 : 2,
        text: cap[1]
      });
      continue;
    }

    if (cap = block.hr.exec(str)) {
      str = str.substring(cap[0].length);
      tokens.push({
        type: 'hr'
      });
      continue;
    }

    if (cap = block.blockquote.exec(str)) {
      str = str.substring(cap[0].length);
      tokens.push({
        type: 'blockquote_start'
      });
      cap = cap[0].replace(/^ *>/gm, '');
      block.token(cap, tokens);
      tokens.push({
        type: 'blockquote_end'
      });
      continue;
    }

    if (cap = block.list.exec(str)) {
      str = str.substring(cap[0].length);

      tokens.push({
        type: 'list_start',
        ordered: isFinite(cap[2])
      });

      loose = /\n *\n *(?:[*+-]|\d+\.)/.test(cap[0]);

      // get each top-level item
      cap = cap[0].match(
        /^( *)([*+-]|\d+\.)[^\n]*(?:\n(?!\1(?:\2|\d+\.))[^\n]*)*/gm
      );

      each(cap, function(item) {
        // remove the list items sigil
        // so its seen as the next token
        item = item.replace(/^ *([*+-]|\d+\.) */, '');
        // outdent whatever the
        // list item contains, hacky
        var space = /\n( +)/.exec(item);
        if (space) {
          space = new RegExp('^' + space[1], 'gm');
          item = item.replace(space, '');
        }
        tokens.push({
          type: loose
            ? 'loose_item_start'
            : 'list_item_start'
        });
        block.token(item, tokens);
        tokens.push({
          type: 'list_item_end'
        });
      });

      tokens.push({
        type: 'list_end'
      });

      continue;
    }

    if (cap = block.html.exec(str)) {
      str = str.substring(cap[0].length);
      tokens.push({
        type: 'html',
        text: cap[0]
      });
      continue;
    }

    if (cap = block.text.exec(str)) {
      str = str.substring(cap[0].length);
      tokens.push({
        type: 'text',
        text: cap[0]
      });
      continue;
    }
  }

  return tokens;
};

/**
 * Inline Processing
 */

var inline = {
  escape: /^\\([\\`*{}\[\]()#+\-.!_])/,
  autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
  tag: /^|^<\/?\w+[^>]*>/,
  link: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]\s*\(([^\)]*)\)/,
  reflink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]\s*\[([^\]]*)\]/,
  nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
  strong: /^__([^\0]+?)__(?!_)|^\*\*([^\0]+?)\*\*(?!\*)/,
  em: /^_([^_]+)_|^\*([^*]+)\*/,
  code: /^`([^`]+)`|^``([^\0]+?)``/,
  br: /^ {2,}\n(?!\s*$)/,
  text: /^/
};

// hacky, but performant
inline.text = (function() {
  var body = [];

  (function push(rule) {
    rule = inline[rule].source;
    body.push(rule.replace(/(^|[^\[])\^/g, '$1'));
    return push;
  })
  ('escape')
  ('tag')
  ('nolink')
  ('strong')
  ('em')
  ('code')
  ('br');

  return new
    RegExp('^[^\\0]+?(?=' + body.join('|') + '|$)');
})();

/**
 * Inline Lexer
 */

inline.lexer = function(str) {
  var out = ''
    , links = tokens.links
    , link
    , text
    , href
    , cap;

  while (str) {
    if (cap = inline.escape.exec(str)) {
      str = str.substring(cap[0].length);
      out += cap[1];
      continue;
    }

    if (cap = inline.autolink.exec(str)) {
      str = str.substring(cap[0].length);
      if (cap[2] === '@') {
        text = cap[1][6] === ':'
          ? mangle(cap[1].substring(7))
          : mangle(cap[1]);
        href = mangle('mailto:') + text;
      } else {
        text = escape(cap[1]);
        href = text;
      }
      out += ''
        + text
        + '';
      continue;
    }

    if (cap = inline.tag.exec(str)) {
      str = str.substring(cap[0].length);
      out += cap[0];
      continue;
    }

    if (cap = inline.link.exec(str)) {
      str = str.substring(cap[0].length);
      text = /^\s*<?([^\s]*?)>?(?:\s+"([^\n]+)")?\s*$/.exec(cap[2]);
      link = {
        href: text[1],
        title: text[2]
      };
      out += mlink(cap, link);
      continue;
    }

    if ((cap = inline.reflink.exec(str))
        || (cap = inline.nolink.exec(str))) {
      str = str.substring(cap[0].length);
      link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
      link = links[link];
      if (!link) {
        out += cap[0][0];
        str = cap[0].substring(1) + str;
        continue;
      }
      out += mlink(cap, link);
      continue;
    }

    if (cap = inline.strong.exec(str)) {
      str = str.substring(cap[0].length);
      out += ''
        + inline.lexer(cap[2] || cap[1])
        + '';
      continue;
    }

    if (cap = inline.em.exec(str)) {
      str = str.substring(cap[0].length);
      out += ''
        + inline.lexer(cap[2] || cap[1])
        + '';
      continue;
    }

    if (cap = inline.code.exec(str)) {
      str = str.substring(cap[0].length);
      out += ''
        + escape(cap[2] || cap[1])
        + '';
      continue;
    }

    if (cap = inline.br.exec(str)) {
      str = str.substring(cap[0].length);
      out += '
';
      continue;
    }

    if (cap = inline.text.exec(str)) {
      str = str.substring(cap[0].length);
      out += escape(cap[0]);
      continue;
    }
  }

  return out;
};

var mlink = function(cap, link) {
  if (cap[0][0] !== '!') {
    return ''
      + inline.lexer(cap[1])
      + '';
  } else {
    return '';
  }
};

/**
 * Parsing
 */

var tokens
  , token;

var next = function() {
  return token = tokens.pop();
};

var tok = function() {
  switch (token.type) {
    case 'space':
      return '';
    case 'hr':
      return '
';
    case 'heading':
      return '<h' + token.depth + '>'
        + inline.lexer(token.text)
        + '</h' + token.depth + '>';
    case 'block':
      return '
'
        + escape(token.text)
        + '
';
    case 'blockquote_start':
      var body = [];
      while (next().type !== 'blockquote_end') {
        body.push(tok());
      }
      return '
'
        + body.join('')
        + '
';
    case 'list_start':
      var type = token.ordered ? 'ol' : 'ul'
        , body = [];
      while (next().type !== 'list_end') {
        body.push(tok());
      }
      return '<' + type + '>'
        + body.join('')
        + '</' + type + '>';
    case 'list_item_start':
      var body = [];
      while (next().type !== 'list_item_end') {
        body.push(token.type === 'text'
          ? text()
          : tok());
      }
      return '
'
        + body.join(' ')
        + '
';
    case 'loose_item_start':
      var body = [];
      while (next().type !== 'list_item_end') {
        body.push(tok());
      }
      return '
'
        + body.join(' ')
        + '
';
    case 'html':
      return inline.lexer(token.text);
    case 'text':
      return '
'
        + text()
        + '
';
 }
};

var text = function() {
  var body = [ token.text ]
    , top;

  while ((top = tokens[tokens.length-1])
         && top.type === 'text') {
    body.push(next().text);
  }

  return inline.lexer(body.join('\n'));
};

var parse = function(src) {
  tokens = src.reverse();

  var out = [];
  while (next()) {
    out.push(tok());
  }

  tokens = null;
  token = null;

  return out.join(' ');
};

/**
 * Helpers
 */

var escape = function(html) {
  return html
    .replace(/&/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"')
    .replace(/'/g, ''');
};

var mangle = function(str) {
  var out = ''
    , ch
    , i = 0
    , l = str.length;

  for (; i < l; i++) {
    ch = str.charCodeAt(i);
    if (Math.random() > 0.5) {
      ch = 'x' + ch.toString(16);
    }
    out += '&#' + ch + ';';
  }

  return out;
};

var each = function(obj, func) {
  var i = 0, l = obj.length;
  for (; i < l; i++) func(obj[i]);
};

/**
 * Expose
 */

var marked = function(str) {
  return parse(block.lexer(str));
};

marked.parser = parse;
marked.lexer = block.lexer;

if (typeof module !== 'undefined') {
  module.exports = marked;
} else {
  this.marked = marked;
}

}).call(this);
<|endoftext|>


<commit_before>'use strict';
/**
 * Created by Online By Design LLC.
 *
 * The master is in charge of keeping track of task requests from the workers and assigning tasks as they become available.
 * The master also manages adding and updating tasks in the mongodb
 */

/**
 * Master is initialized here
 *
 * @param config - Configuration options
 */
module.exports = function (config) {
    var io = require('socket.io')(config.master.port);
    var mongoose = require('mongoose');
    var _ = require('lodash');
    var async = require('async');
    var moment = require('moment');
    var log = require('../logger');

    // Error if logger.js is not loaded.
    if (!log) return console.error('Please create logger.js in the root directory.  You may copy and save logger.example.js as logger.js.  See the documentation for more information.');

    // Load models
    var Task = require('../models/tasks');

    // Keep a variable so we know if we are in the process of assigning tasks.
    var assigningTasks = false;
    var timeout = config.runner.worker.timeout || 300000; // 5 minutes is the default timeout time for master
    timeout += 10000; //Add 10 seconds so the time is slightly longer than that for the worker

    // Module configuration
    mongoose.connect(config.master.mongoURL);
    log.info('Started Master');

    // Connect to database and get the list of tasks
    var db = mongoose.connection;
    db.on('error', function (err) {
        log.error(err, 'Failed to connect to mongo database');
    }); // TODO: test this out

    db.once('open', function () {

        // Find all of the tasks that are neither complete nor assigned
        Task //.where('task.assigned').exists(false)
            .where('task.completed').exists(false)
            .sort('-priority created')
            .exec(function (err, tasks) {
                if (err) return log.error(err, 'Error when finding tasks');

                if (tasks.length > 0) {
                    assignTasks();
                }
            });
    });

    /**
     * When a worker establishes/reestablishes connection
     */
    io.on('connection', function (socket) {
        log.info('Worker connected.  (', findWorkers().length, ')');

        /**
         * When the worker disconnects
         */
        socket.on('disconnect', function () {
            log.warn('Worker disconnected.  (', findWorkers().length, ')');
        });

        /**
         * When the socket has an error
         */
        socket.on('error', function (err) {
            log.error({err: err}, "A socket had an error.");
        });

        /**
         * When the worker requests a task
         */
        socket.on('task:request', function () {
            socket._idleWorker = true;
            log.info('Worker requesting a new task');
            assignTasks();
        });

        /**
         * When a worker worker finishes a task
         */
        socket.on('task:done', function (taskJSON) {
            clearTimeout(socket._timer);
            // Update the task and save it to the database.
            var taskParsed = JSON.parse(taskJSON);
            Task.findById(taskParsed._id, function (err, task) {
                if (err) log.error({err: err}, 'Error finding task to mark as complete');

                task.assigned.completed = moment().unix();
                task.assigned.status = 'done';
                task.save(function (err) {
                    if (err) return log.error({err: err}, 'Error updating task status as done');

                    log.info({task: task}, 'Task completed');
                    assignTasks();
                });
            });
        });

        /**
         * When a worker worker has a task error
         */
        socket.on('task:error', function (err, taskJSON) {
            clearTimeout(socket._timer);
            var taskParsed = JSON.parse(taskJSON);

            // Delete assigned state so task can be reassigned
            Task.findById(taskParsed._id, function (err, task) {
                if (err) return log.error({err: err}, 'Error finding task passed in from an error');

                task.assigned.status = 'error';
                task.save(function (err) {
                    if (err) log.error({err: err}, 'Error updating task status as error');

                    log.error({task: task}, 'Task error');
                });
            });
        });

        /**
         * When a worker wants to add tasks to the list
         *
         * @param Array tasks - An array of task objects (in JSON format)
         */
        socket.on('task:add', function (tasks) {
            tasks = _.isArray(tasks) ? tasks : [tasks];

            async.each(tasks, function (task, next) {

                // Only allow one task with a given module, params, and dependencies
                Task.update({
                    module: task.module,
                    params: task.params,
                    dependencies: task.dependencies
                }, task, {upsert: true}, function (err) {
                    if (err) return next(err, task);

                    log.info({task: task}, 'Task added');
                    socket.emit('task:add:confirm');
                    next();
                });
            }, function (err, task) {
                if (err) return log.error({err: err, task: task}, 'Error while adding task');

                assignTasks();
            });
        });
    });

    /***********************************************************************************************************************
     * Helper functions
     **********************************************************************************************************************/

    /**
     * Assigns the next available task to the next available worker while there is still an available task and worker.
     * This is done synchronously since multiple instances of it may be running at a time.
     */
    function assignTasks() {
        if (!assigningTasks) {
            assigningTasks = true;

            var socket;
            var task;

            // While there is an idle worker
            async.during(
                function (callback) {
                    // Find an idle worker.
                    socket = _.find(findWorkers(), function (worker) {return worker._idleWorker});

                    // Find an available task
                    findNextTask(function (nextTask) {
                        log.info({task: nextTask}, 'Task found');
                        callback(null, !_.isEmpty(socket) && !_.isEmpty(nextTask));
                    });
                },
                function (next) {
                    // Mark the task as assigned
                    task.assigned.who = 'workername'; // TODO: Pull the worker name from the socket
                    task.assigned.when = moment().unix();
                    task.assigned.status = 'assigned';

                    task.save(function (err) {
                        if (err) return next(err);

                        socket.emit('task:run', JSON.stringify(task));
                        socket._idleWorker = false;

                        // Use global timeout unless specific task timeout is set.
                        var taskTimeout = task.timeout || timeout;

                        // Set a timer which will deal with workers which die while working.
                        socket._timer = setTimeout((function () {
                            var socket = this.socket;
                            var task = this.task;
                            var message = 'Task timed out without response from worker.';
                            var err = 'Task assigned ' + moment.unix(task.assigned.when).format();
                            socket._idleWorker = true;
                            socket.emit('task:cancel', JSON.stringify(task));
                            log.error({err: err}, message);

                            if (task) {
                                task.assigned = undefined;

                                task.save(function (err) {
                                    if (err) return log.error({err: err}, 'Error un-assigning task assignment to mongodb');

                                    assignTasks();
                                });
                            } else {
                                assignTasks();
                            }
                        }).bind({task: task, socket: socket}), taskTimeout);

                        // Log that the task was paired and sent.
                        log.info({task: task}, 'Task sent');
                        next();
                    });
                },
                function (err) {
                    if (err) log.error({err: err}, 'Error assigning task');
                    assigningTasks = false;
                }
            );
        }
    }

    /**
     * Finds the next available task.
     * Next available task is one that:
     *  - has not been assigned
     *  - is sorted by priority descending
     *  - is sorted by created ascending
     *  and:
     *  - has no dependencies unmet
     */
    function findNextTask(callback) {
        // Find all tasks that have not been assigned yet
        Task.where('assigned').exists(false)
            .sort('-priority created')
            .exec(function (err, unassignedTasks) {
                if (err) return log.error(err, 'Unable to connect to database to get an unassigned task');
                log.info(unassignedTasks.length, 'Unassigned task count');
                // Find a task whose dependencies are complete
                async.detectSeries(unassignedTasks, function (unassignedTask, next) {
                    Task.where('assigned.completed').exists(true).or(unassignedTask.dependencies).exec(function (err, completedDependencyTasks) {
                        if (err) return log.error(err, 'Unable to connect to database to get dependencies');
                        log.info(unassignedTask, completedDependencyTasks.length, 'Completed dependencies');
                        // If every dependency matches any completedDependency then we have found the next task
                        next(_.every(unassignedTask.dependencies, function (dependency) {
                            return _.any(completedDependencyTasks, function (completedDependencyTask) {
                                return _.isMatch(completedDependencyTask.toObject(), dependency);
                            })
                        }));
                    });
                }, function (taskToAssign) {
                    callback(taskToAssign);
                });
            }
        );
    }

    /**
     * Searches for workers connected to the socket.io master (Copied from some where online)
     *
     * @param {String} [namespace] - Filter workers by namespace
     * @param {String} [room] - Filter workers by room
     * @returns {Array} - List of workers
     */
    function findWorkers(namespace, room) {
        var workers = [], ns = io.of(namespace || "/");

        if (ns) {
            for (var id in ns.connected) {
                if (room) {
                    var index = ns.connected[id].rooms.indexOf(room);
                    if (index !== -1) {
                        workers.push(ns.connected[id]);
                    }
                } else {
                    workers.push(ns.connected[id]);
                }
            }
        }
        return workers;
    }
}
;
<commit_msg>Added a log for when a task is found in a task request.<commit_after>'use strict';
/**
 * Created by Online By Design LLC.
 *
 * The master is in charge of keeping track of task requests from the workers and assigning tasks as they become available.
 * The master also manages adding and updating tasks in the mongodb
 */

/**
 * Master is initialized here
 *
 * @param config - Configuration options
 */
module.exports = function (config) {
    var io = require('socket.io')(config.master.port);
    var mongoose = require('mongoose');
    var _ = require('lodash');
    var async = require('async');
    var moment = require('moment');
    var log = require('../logger');

    // Error if logger.js is not loaded.
    if (!log) return console.error('Please create logger.js in the root directory.  You may copy and save logger.example.js as logger.js.  See the documentation for more information.');

    // Load models
    var Task = require('../models/tasks');

    // Keep a variable so we know if we are in the process of assigning tasks.
    var assigningTasks = false;
    var timeout = config.runner.worker.timeout || 300000; // 5 minutes is the default timeout time for master
    timeout += 10000; //Add 10 seconds so the time is slightly longer than that for the worker

    // Module configuration
    mongoose.connect(config.master.mongoURL);
    log.info('Started Master');

    // Connect to database and get the list of tasks
    var db = mongoose.connection;
    db.on('error', function (err) {
        log.error(err, 'Failed to connect to mongo database');
    }); // TODO: test this out

    db.once('open', function () {

        // Find all of the tasks that are neither complete nor assigned
        Task //.where('task.assigned').exists(false)
            .where('task.completed').exists(false)
            .sort('-priority created')
            .exec(function (err, tasks) {
                if (err) return log.error(err, 'Error when finding tasks');

                if (tasks.length > 0) {
                    assignTasks();
                }
            });
    });

    /**
     * When a worker establishes/reestablishes connection
     */
    io.on('connection', function (socket) {
        log.info('Worker connected.  (', findWorkers().length, ')');

        /**
         * When the worker disconnects
         */
        socket.on('disconnect', function () {
            log.warn('Worker disconnected.  (', findWorkers().length, ')');
        });

        /**
         * When the socket has an error
         */
        socket.on('error', function (err) {
            log.error({err: err}, "A socket had an error.");
        });

        /**
         * When the worker requests a task
         */
        socket.on('task:request', function () {
            socket._idleWorker = true;
            log.info('Worker requesting a new task');
            assignTasks();
        });

        /**
         * When a worker worker finishes a task
         */
        socket.on('task:done', function (taskJSON) {
            clearTimeout(socket._timer);
            // Update the task and save it to the database.
            var taskParsed = JSON.parse(taskJSON);
            Task.findById(taskParsed._id, function (err, task) {
                if (err) log.error({err: err}, 'Error finding task to mark as complete');

                task.assigned.completed = moment().unix();
                task.assigned.status = 'done';
                task.save(function (err) {
                    if (err) return log.error({err: err}, 'Error updating task status as done');

                    log.info({task: task}, 'Task completed');
                    assignTasks();
                });
            });
        });

        /**
         * When a worker worker has a task error
         */
        socket.on('task:error', function (err, taskJSON) {
            clearTimeout(socket._timer);
            var taskParsed = JSON.parse(taskJSON);

            // Delete assigned state so task can be reassigned
            Task.findById(taskParsed._id, function (err, task) {
                if (err) return log.error({err: err}, 'Error finding task passed in from an error');

                task.assigned.status = 'error';
                task.save(function (err) {
                    if (err) log.error({err: err}, 'Error updating task status as error');

                    log.error({task: task}, 'Task error');
                });
            });
        });

        /**
         * When a worker wants to add tasks to the list
         *
         * @param Array tasks - An array of task objects (in JSON format)
         */
        socket.on('task:add', function (tasks) {
            tasks = _.isArray(tasks) ? tasks : [tasks];

            async.each(tasks, function (task, next) {

                // Only allow one task with a given module, params, and dependencies
                Task.update({
                    module: task.module,
                    params: task.params,
                    dependencies: task.dependencies
                }, task, {upsert: true}, function (err) {
                    if (err) return next(err, task);

                    log.info({task: task}, 'Task added');
                    socket.emit('task:add:confirm');
                    next();
                });
            }, function (err, task) {
                if (err) return log.error({err: err, task: task}, 'Error while adding task');

                assignTasks();
            });
        });
    });

    /***********************************************************************************************************************
     * Helper functions
     **********************************************************************************************************************/

    /**
     * Assigns the next available task to the next available worker while there is still an available task and worker.
     * This is done synchronously since multiple instances of it may be running at a time.
     */
    function assignTasks() {
        if (!assigningTasks) {
            assigningTasks = true;

            var socket;
            var task;

            // While there is an idle worker
            async.during(
                function (callback) {
                    // Find an idle worker.
                    socket = _.find(findWorkers(), function (worker) {return worker._idleWorker});

                    // Find an available task
                    findNextTask(function (nextTask) {
                        log.info({task: nextTask}, 'Task found');
                        task = nextTask;
                        return callback(null, !_.isEmpty(socket) && !_.isEmpty(nextTask));
                    });
                },
                function (next) {
                    // Mark the task as assigned
                    task.assigned.who = 'workername'; // TODO: Pull the worker name from the socket
                    task.assigned.when = moment().unix();
                    task.assigned.status = 'assigned';

                    task.save(function (err) {
                        if (err) return next(err);

                        socket.emit('task:run', JSON.stringify(task));
                        socket._idleWorker = false;

                        // Use global timeout unless specific task timeout is set.
                        var taskTimeout = task.timeout || timeout;

                        // Set a timer which will deal with workers which die while working.
                        socket._timer = setTimeout((function () {
                            var socket = this.socket;
                            var task = this.task;
                            var message = 'Task timed out without response from worker.';
                            var err = 'Task assigned ' + moment.unix(task.assigned.when).format();
                            socket._idleWorker = true;
                            socket.emit('task:cancel', JSON.stringify(task));
                            log.error({err: err}, message);

                            if (task) {
                                task.assigned = undefined;

                                task.save(function (err) {
                                    if (err) return log.error({err: err}, 'Error un-assigning task assignment to mongodb');

                                    assignTasks();
                                });
                            } else {
                                assignTasks();
                            }
                        }).bind({task: task, socket: socket}), taskTimeout);

                        // Log that the task was paired and sent.
                        log.info({task: task}, 'Task sent');
                        next();
                    });
                },
                function (err) {
                    if (err) log.error({err: err}, 'Error assigning task');
                    assigningTasks = false;
                }
            );
        }
    }

    /**
     * Finds the next available task.
     * Next available task is one that:
     *  - has not been assigned
     *  - is sorted by priority descending
     *  - is sorted by created ascending
     *  and:
     *  - has no dependencies unmet
     */
    function findNextTask(callback) {
        // Find all tasks that have not been assigned yet
        Task.where('assigned').exists(false)
            .sort('-priority created')
            .exec(function (err, unassignedTasks) {
                if (err) return log.error(err, 'Unable to connect to database to get an unassigned task');
                log.info(unassignedTasks.length, 'Unassigned task count');
                // Find a task whose dependencies are complete
                async.detectSeries(unassignedTasks, function (unassignedTask, next) {
                    Task.where('assigned.completed').exists(true).or(unassignedTask.dependencies).exec(function (err, completedDependencyTasks) {
                        if (err) return log.error(err, 'Unable to connect to database to get dependencies');
                        log.info(unassignedTask, completedDependencyTasks.length, 'Completed dependencies');
                        // If every dependency matches any completedDependency then we have found the next task
                        next(_.every(unassignedTask.dependencies, function (dependency) {
                            return _.any(completedDependencyTasks, function (completedDependencyTask) {
                                return _.isMatch(completedDependencyTask.toObject(), dependency);
                            })
                        }));
                    });
                }, function (taskToAssign) {
                    callback(taskToAssign);
                });
            }
        );
    }

    /**
     * Searches for workers connected to the socket.io master (Copied from some where online)
     *
     * @param {String} [namespace] - Filter workers by namespace
     * @param {String} [room] - Filter workers by room
     * @returns {Array} - List of workers
     */
    function findWorkers(namespace, room) {
        var workers = [], ns = io.of(namespace || "/");

        if (ns) {
            for (var id in ns.connected) {
                if (room) {
                    var index = ns.connected[id].rooms.indexOf(room);
                    if (index !== -1) {
                        workers.push(ns.connected[id]);
                    }
                } else {
                    workers.push(ns.connected[id]);
                }
            }
        }
        return workers;
    }
}
;
<|endoftext|>


<commit_before>var path = require('path');
var _ = require('underscore');
var Source = require('./sources/source');
var Command = require('./command');
var fs = require('fs');
var fstream = require('fstream');
var wrench = require('wrench');
var spawn = require('child_process').spawn;

// A 'Meteor' refers to a single commit (branch, tag) of a version of the core meteor
//
// They are located on disk in ~/.meteorite/meteors.
// When you install a meteor into a project, it copies everything into
// ./.meteor/meteorite to create a 'custom' version of meteor for you to install packages into.
Meteor = function(config) {

  // Config defaults
  config = config || {};
  if (config === {})
    this.defaultMeteor = true;

  config.git = config.git || 'https://github.com/meteor/meteor.git';
  config.keepGitDir = true;

  // Prepare source
  var root = Meteor.installRoot();
  this.source = Source.prepare(root, config);

  // TODO -- this is a bit coarse. but it avoids multiple installs for now
  this.prepared = fs.existsSync(this.source.path);
};

// download the repo + install dev_bundle
Meteor.prototype.prepare = function(fn) {
  if (this.source.isLoaded())
    this.install(fn);
  else
    this.fetch(fn);
};

Meteor.prototype.fetch = function(fn) {
  var self = this;

  console.log('Fetching Meteor ' + self.source + '...');

  self.source.fetch(function() {
    self.install(fn);
  });
};

Meteor.prototype.install = function(fn) {
  var self = this;

  self.prepared = true;

  // ensure the dev_bundle is installed.
  var dev_bundle = path.join(self.source.path, 'dev_bundle');

  if (!fs.existsSync(dev_bundle)) {
    // a little hack. meteor --help installs the dev bundle before doing anything else
    console.log('Downloading Meteor development bundle...');
    var meteor = spawn('./meteor', ['--help'], {cwd: self.source.path});

    // just output the status bar
    meteor.stderr.pipe(process.stderr);
    meteor.on('exit', fn);
  } else {
    fn();
  }
};

// check that the above has happened
Meteor.prototype.ensurePrepared = function(fn) {
  if (this.prepared) {
    fn();
  } else {
    this.prepare(fn);
  }
};

// run a command using just this checked-out version of meteor
Meteor.prototype.execute = function(args, fn) {
  var self = this;

  self.ensurePrepared(function() {
    Command.execute(path.join(self.source.packagePath(), 'meteor'), args, fn);
  });
};

// install a downloaded meteor into a project
Meteor.prototype.installInto = function(project, fn) {

  if (!project.isMeteorInstalled()) {

    console.log('Install custom meteor build...');

    // Copy meteor from ~/.meteorite/meteors to ./.meteor/meteorite
    var reader = fstream.Reader(this.source.path);
    var writer = fstream.Writer(project.installRoot);

    // Cleanup
    writer.on('close', fn);

    // Do it already
    reader.pipe(writer);

  } else {
    fn();
  }
};

Meteor.prototype.toJson = function(lock) {
  return this.source.toJson(lock);
};

Meteor.prototype.equals = function(otherMeteor) {

  return this.source.equals(otherMeteor.source);
};

Meteor.installRoot = function() {
  return path.join(Meteorite.root(), 'meteors');
};

module.exports = Meteor;
<commit_msg>delete installRoot before copying<commit_after>var path = require('path');
var _ = require('underscore');
var Source = require('./sources/source');
var Command = require('./command');
var fs = require('fs');
var fstream = require('fstream');
var wrench = require('wrench');
var spawn = require('child_process').spawn;

// A 'Meteor' refers to a single commit (branch, tag) of a version of the core meteor
//
// They are located on disk in ~/.meteorite/meteors.
// When you install a meteor into a project, it copies everything into
// ./.meteor/meteorite to create a 'custom' version of meteor for you to install packages into.
Meteor = function(config) {

  // Config defaults
  config = config || {};
  if (config === {})
    this.defaultMeteor = true;

  config.git = config.git || 'https://github.com/meteor/meteor.git';
  config.keepGitDir = true;

  // Prepare source
  var root = Meteor.installRoot();
  this.source = Source.prepare(root, config);

  // TODO -- this is a bit coarse. but it avoids multiple installs for now
  this.prepared = fs.existsSync(this.source.path);
};

// download the repo + install dev_bundle
Meteor.prototype.prepare = function(fn) {
  if (this.source.isLoaded())
    this.install(fn);
  else
    this.fetch(fn);
};

Meteor.prototype.fetch = function(fn) {
  var self = this;

  console.log('Fetching Meteor ' + self.source + '...');

  self.source.fetch(function() {
    self.install(fn);
  });
};

Meteor.prototype.install = function(fn) {
  var self = this;

  self.prepared = true;

  // ensure the dev_bundle is installed.
  var dev_bundle = path.join(self.source.path, 'dev_bundle');

  if (!fs.existsSync(dev_bundle)) {
    // a little hack. meteor --help installs the dev bundle before doing anything else
    console.log('Downloading Meteor development bundle...');
    var meteor = spawn('./meteor', ['--help'], {cwd: self.source.path});

    // just output the status bar
    meteor.stderr.pipe(process.stderr);
    meteor.on('exit', fn);
  } else {
    fn();
  }
};

// check that the above has happened
Meteor.prototype.ensurePrepared = function(fn) {
  if (this.prepared) {
    fn();
  } else {
    this.prepare(fn);
  }
};

// run a command using just this checked-out version of meteor
Meteor.prototype.execute = function(args, fn) {
  var self = this;

  self.ensurePrepared(function() {
    Command.execute(path.join(self.source.packagePath(), 'meteor'), args, fn);
  });
};

// install a downloaded meteor into a project
Meteor.prototype.installInto = function(project, fn) {

  if (!project.isMeteorInstalled()) {

    console.log('Install custom meteor build...');

    // Blow away .meteor/meteorite because in the case of
    // an upgrade it will be present
    wrench.mkdirSyncRecursive(project.installRoot);

    // Copy meteor from ~/.meteorite/meteors to ./.meteor/meteorite
    var reader = fstream.Reader(this.source.path);
    var writer = fstream.Writer(project.installRoot);

    // Cleanup
    writer.on('close', fn);

    // Do it already
    reader.pipe(writer);

  } else {
    fn();
  }
};

Meteor.prototype.toJson = function(lock) {
  return this.source.toJson(lock);
};

Meteor.prototype.equals = function(otherMeteor) {

  return this.source.equals(otherMeteor.source);
};

Meteor.installRoot = function() {
  return path.join(Meteorite.root(), 'meteors');
};

module.exports = Meteor;
<|endoftext|>


<commit_before>(function () {
  'use strict';

  var defined = {};
  var isFunction;
  var addModule;
  var isString;
  var require;
  var module;
  var clear;

  addModule = function (name) {
    if (!defined[name]) {
      defined[name] = module.exports;
    } else {
      throw new Error('module ' + name + ' already exists');
    }
  };

  clear = function () {
    delete module.exports;
  };

  isFunction = function (obj) {
    return Object.prototype.toString.call(obj) === '[object Function]';
  };

  isString = function (value) {
    return typeof value === 'string';
  };

  module = function (name, fn) {
    if (name && isString(name) && fn && isFunction(fn)) {
      fn();
      addModule(name);
      clear();
    }
  };

  require = function (name) {
    if (name && isString(name)) {
      if (defined[name]) {
        return defined[name];
      } else {
        throw new Error('module ' + name + ' was not defined');
      }
    } else {
      throw new Error('module name is not a string');
    }
  };

  window.require = require;
  window.module = module;
}());
<commit_msg>Minor changes to lib/mod-me.js<commit_after>(function () {
  'use strict';

  var defined = {};
  var isFunction;
  var addModule;
  var isString;
  var require;
  var module;
  var clear;

  addModule = function (name) {
    if (!defined[name]) {
      defined[name] = module.exports;
    } else {
      throw new Error('Module ' + name + ' already exists.');
    }
  };

  clear = function () {
    delete module.exports;
  };

  isFunction = function (obj) {
    return Object.prototype.toString.call(obj) === '[object Function]';
  };

  isString = function (value) {
    return typeof value === 'string';
  };

  module = function (name, fn) {
    if (name && isString(name) && fn && isFunction(fn)) {
      fn();
      addModule(name);
      clear();
    }
  };

  require = function (name) {
    if (name && isString(name)) {
      if (defined[name]) {
        return defined[name];
      } else {
        throw new Error('Module ' + name + ' was not defined.');
      }
    } else {
      throw new Error('Module\'s name is not a string.');
    }
  };

  window.require = require;
  window.module = module;
}());
<|endoftext|>


<commit_before>var Joi = require('joi');

var models = module.exports = {};

models.Application = {
  tableName: 'applications',
	hashKey: 'appId',
	schema: {
  	appId: Joi.string(),
    orgId: Joi.string().required(),
    created: Joi.date().default(Date.now, 'created'),
    updated: Joi.date().default(Date.now, 'updated'),
    ownerId: Joi.string().required(),
    requireSsl: Joi.boolean().default(false),
    trafficControlEnabled: Joi.boolean().default(false),
    // deployments is a Map where the top level keys are the env names and the values
    // is an array of traffic rules. Each rule consists of a version attribute and a rule
    // attribute.
    trafficRules: Joi.object().pattern(/[a-z]+/, Joi.array(Joi.object().keys({
      version: Joi.string(),
      rule: Joi.string()
    }))).default({}),

    // Addon specific settings. The keys in the object is the name of the addon.
    addOnSettings: Joi.object().pattern(/[a-z]+/, Joi.object()).default({}),
    env: Joi.object().default({})
  },
  indexes: [
    {
      name: 'orgIdIndex',
      type: 'global',
      hashKey: 'orgId',
      projection: {
        ProjectionType: 'KEYS_ONLY'
      }
    },
    {
      name: 'ownerIdIndex',
      type: 'global',
      hashKey: 'ownerId',
      projection: {
        ProjectionType: 'KEYS_ONLY'
      }
    }
  ]
};

models.User = {
  tableName: 'users',
  hashKey: 'userId',
  schema: {
    userId: Joi.string(),
    providerUserId: Joi.string().required(),
    username: Joi.string().required(),
    provider: Joi.string().required(),
    email: Joi.string(),
    avatar: Joi.string(),
    defaultOrgId: Joi.string(),
    lastLogin: Joi.date(),
    joined: Joi.date().default(Date.now, 'joined')
  },
  indexes : [
    {
      name : 'providerUserIndex',
      hashKey : 'providerUserId',
      rangeKey : 'provider',
      type : 'global'
    }
  ]
};

models.Organization = {
  tableName: 'organizations',
  hashKey: 'orgId',
  schema: {
    orgId: Joi.string(),
    name: Joi.string().required(),
    createdDate: Joi.date().default(Date.now, 'createdDate'),
    activatedDate: Joi.date(),
    activated: Joi.boolean().default(false),
    terminated: Joi.boolean().default(false),
    terminationDate: Joi.date(),
    terminatedBy: Joi.string(),
    operationLimit: Joi.number(),
    plan: Joi.string(),
    monthlyRate: Joi.number(),
    trialStart: Joi.date(),
    trialEnd: Joi.date(),
    ownerId: Joi.string().required(),
    source: Joi.string(), // Value like 'bitbucket', 'aerobatic', etc.

    // Default environment pipeline as an ordered list
    // Apps can only be promoted in this order
    environments: Joi.array(Joi.string())
  }
};

models.OrgMember = {
  tableName: 'orgMembers',
  hashKey: 'orgId',
  rangeKey: 'userId',
  schema: {
    orgId: Joi.string(),
    userId: Joi.string(),
    created: Joi.date().default(Date.now, 'joined'),
    role: Joi.string().required()
  },
  indexes: [
    {
      name: 'userIdIndex',
      type: 'global',
      hashKey: 'userId'
    }
  ]
};

models.Domain = {
  tableName: 'domains',
	hashKey: 'domain',
  schema: {
    domain: Joi.string(),
    appId: Joi.string().required()
  },
  indexes: [
    {
      name: 'appIdIndex',
      hashKey: 'appId',
      type: 'global'
    }
  ]
};

models.AppName = {
  tableName: 'appName',
  hashKey: 'name',
  schema: {
    name: Joi.string(),
    appId: Joi.string().required()
  },
  indexes: [
    {
      name: 'appIdIndex',
      type: 'global',
      hashKey: 'appId'
    }
  ]
};

models.Version = {
  tableName: 'versions',
	hashKey: 'appId',
	rangeKey: 'versionId',
  schema: {
    appId: Joi.string(),
    versionId: Joi.string(),
    versionNum: Joi.number(),
    name: Joi.string().required(),
    userId: Joi.string().required(),
    created: Joi.date().default(Date.now, 'created'),
    message: Joi.string(),
    status: Joi.string().required().valid(['initiated', 'complete', 'failed']),

    // The JSON app manifest for this version
    manifest: Joi.object().required()
  },
  indexes: [
    {
    	name: 'appIdVersionNumIndex',
    	type: 'local',
    	hashKey: 'appId',
    	rangeKey: 'versionNum',
      projection: { NonKeyAttributes: [ 'name','userId','created','message' ], ProjectionType: 'INCLUDE' }
    }
  ]
};

models.DailyOperations = {
  tableName: 'dailyOperations',
  hashKey: 'orgId',
  rangeKey: 'dateAppId', // example 2015-03-appid
  schema: {
    orgId: Joi.string(),
    dateAppId: Joi.string(),
    appId: Joi.string(),
    total: Joi.number()
    // Additional attributes are created on the fly with names like "op_html-page, op_api-proxy, etc
    // that hold the count of the number of times that operation was invoked.
  },
  indexes: [
    {
      name: 'appIdIndex',
      type: 'global',
      hashKey: 'appId',
      rangeKey: 'dateAppId'
    }
  ]
};

// Generic table for holding key/values where the value is itself a key/value map
models.KeyValueMap = {
  tableName: 'keyValueMap',
  hashKey: 'key',
  schema: {
    key: Joi.string(),
    value: Joi.object().pattern(/[a-z]+/, Joi.object()).default({}),
    createdDate: Joi.date().default(Date.now, 'createdDate')
  }
};

// Audit trail of actions taken including things like: app created, app deleted,
// version deployed, version promoted, version rolled back
// models.AuditTrail = {
//   tableName: 'auditTrail',
//   hashKey: 'orgId',
//   rangeKey: 'timestamp',
//
//   schema: {
//     appId: Joi.string(),
//     versionId: Joi.string(),
//     action: Joi.string(), // created, promoted, rolledback, softLaunch
//     userId: Joi.string(),
//     timestamp: Joi.date().default(Date.now, 'timestamp')
//   }
// };
<commit_msg>Adding Version.error property<commit_after>var Joi = require('joi');

var models = module.exports = {};

models.Application = {
  tableName: 'applications',
	hashKey: 'appId',
	schema: {
  	appId: Joi.string(),
    orgId: Joi.string().required(),
    created: Joi.date().default(Date.now, 'created'),
    updated: Joi.date().default(Date.now, 'updated'),
    ownerId: Joi.string().required(),
    requireSsl: Joi.boolean().default(false),
    trafficControlEnabled: Joi.boolean().default(false),
    // deployments is a Map where the top level keys are the env names and the values
    // is an array of traffic rules. Each rule consists of a version attribute and a rule
    // attribute.
    trafficRules: Joi.object().pattern(/[a-z]+/, Joi.array(Joi.object().keys({
      version: Joi.string(),
      rule: Joi.string()
    }))).default({}),

    // Addon specific settings. The keys in the object is the name of the addon.
    addOnSettings: Joi.object().pattern(/[a-z]+/, Joi.object()).default({}),
    env: Joi.object().default({})
  },
  indexes: [
    {
      name: 'orgIdIndex',
      type: 'global',
      hashKey: 'orgId',
      projection: {
        ProjectionType: 'KEYS_ONLY'
      }
    },
    {
      name: 'ownerIdIndex',
      type: 'global',
      hashKey: 'ownerId',
      projection: {
        ProjectionType: 'KEYS_ONLY'
      }
    }
  ]
};

models.User = {
  tableName: 'users',
  hashKey: 'userId',
  schema: {
    userId: Joi.string(),
    providerUserId: Joi.string().required(),
    username: Joi.string().required(),
    provider: Joi.string().required(),
    email: Joi.string(),
    avatar: Joi.string(),
    defaultOrgId: Joi.string(),
    lastLogin: Joi.date(),
    joined: Joi.date().default(Date.now, 'joined')
  },
  indexes : [
    {
      name : 'providerUserIndex',
      hashKey : 'providerUserId',
      rangeKey : 'provider',
      type : 'global'
    }
  ]
};

models.Organization = {
  tableName: 'organizations',
  hashKey: 'orgId',
  schema: {
    orgId: Joi.string(),
    name: Joi.string().required(),
    createdDate: Joi.date().default(Date.now, 'createdDate'),
    activatedDate: Joi.date(),
    activated: Joi.boolean().default(false),
    terminated: Joi.boolean().default(false),
    terminationDate: Joi.date(),
    terminatedBy: Joi.string(),
    operationLimit: Joi.number(),
    plan: Joi.string(),
    monthlyRate: Joi.number(),
    trialStart: Joi.date(),
    trialEnd: Joi.date(),
    ownerId: Joi.string().required(),
    source: Joi.string(), // Value like 'bitbucket', 'aerobatic', etc.

    // Default environment pipeline as an ordered list
    // Apps can only be promoted in this order
    environments: Joi.array(Joi.string())
  }
};

models.OrgMember = {
  tableName: 'orgMembers',
  hashKey: 'orgId',
  rangeKey: 'userId',
  schema: {
    orgId: Joi.string(),
    userId: Joi.string(),
    created: Joi.date().default(Date.now, 'joined'),
    role: Joi.string().required()
  },
  indexes: [
    {
      name: 'userIdIndex',
      type: 'global',
      hashKey: 'userId'
    }
  ]
};

models.Domain = {
  tableName: 'domains',
	hashKey: 'domain',
  schema: {
    domain: Joi.string(),
    appId: Joi.string().required()
  },
  indexes: [
    {
      name: 'appIdIndex',
      hashKey: 'appId',
      type: 'global'
    }
  ]
};

models.AppName = {
  tableName: 'appName',
  hashKey: 'name',
  schema: {
    name: Joi.string(),
    appId: Joi.string().required()
  },
  indexes: [
    {
      name: 'appIdIndex',
      type: 'global',
      hashKey: 'appId'
    }
  ]
};

models.Version = {
  tableName: 'versions',
	hashKey: 'appId',
	rangeKey: 'versionId',
  schema: {
    appId: Joi.string(),
    versionId: Joi.string(),
    versionNum: Joi.number(),
    name: Joi.string().required(),
    userId: Joi.string().required(),
    created: Joi.date().default(Date.now, 'created'),
    message: Joi.string(),
    status: Joi.string().required().valid(['initiated', 'complete', 'failed']),

    // The version error if status is 'failed'
    error: Joi.string(),

    // The JSON app manifest for this version
    manifest: Joi.object().required()
  },
  indexes: [
    {
    	name: 'appIdVersionNumIndex',
    	type: 'local',
    	hashKey: 'appId',
    	rangeKey: 'versionNum',
      projection: { NonKeyAttributes: [ 'name','userId','created','message' ], ProjectionType: 'INCLUDE' }
    }
  ]
};

models.DailyOperations = {
  tableName: 'dailyOperations',
  hashKey: 'orgId',
  rangeKey: 'dateAppId', // example 2015-03-appid
  schema: {
    orgId: Joi.string(),
    dateAppId: Joi.string(),
    appId: Joi.string(),
    total: Joi.number()
    // Additional attributes are created on the fly with names like "op_html-page, op_api-proxy, etc
    // that hold the count of the number of times that operation was invoked.
  },
  indexes: [
    {
      name: 'appIdIndex',
      type: 'global',
      hashKey: 'appId',
      rangeKey: 'dateAppId'
    }
  ]
};

// Generic table for holding key/values where the value is itself a key/value map
models.KeyValueMap = {
  tableName: 'keyValueMap',
  hashKey: 'key',
  schema: {
    key: Joi.string(),
    value: Joi.object().pattern(/[a-z]+/, Joi.object()).default({}),
    createdDate: Joi.date().default(Date.now, 'createdDate')
  }
};

// Audit trail of actions taken including things like: app created, app deleted,
// version deployed, version promoted, version rolled back
// models.AuditTrail = {
//   tableName: 'auditTrail',
//   hashKey: 'orgId',
//   rangeKey: 'timestamp',
//
//   schema: {
//     appId: Joi.string(),
//     versionId: Joi.string(),
//     action: Joi.string(), // created, promoted, rolledback, softLaunch
//     userId: Joi.string(),
//     timestamp: Joi.date().default(Date.now, 'timestamp')
//   }
// };
<|endoftext|>


<commit_before>// ## Neuron Core: Module Manager
//////////////////////////////////////////////////////////////////////

// ## CommonJS
// Neuron 3.x or newer is not the implementation of any CommonJs proposals
// but only [module/1.0](http://wiki.commonjs.org/wiki/Modules/1.0), one of the stable CommonJs standards.
// And by using neuron and [cortex](http://github.com/cortexjs/cortex), user could write module/1.0 modules.
// Just FORGET `define`.

// ## Naming Conventions of Variables
// All naming of variables should accord to this.

// Take `'a@1.0.0/relative'` for example:

// ### package
// The package which the current module belongs to.
// - name or package name:  {string} package `name`: 'a'
// - package or package id: {string} contains package `name` and `version` and the splitter `'@'`.
//   'a@1.0.0' for instance.

// ### module
// A package is consist of several module objects.
// - mod: {object} the module object. use `mod` instead of `module` to avoid confliction
// - id or module id: the descripter that contains package name, version, and path information
//      {string} for example, `'a@1.0.0/relative'` is a module id(entifier)

// ### version
// Package version: '1.0.0'

// ### main entry
// The module of a package that designed to be accessed from the outside

// ### shadow module and module
// A single module may have different contexts and runtime results
// - mod: the original module definition, including factory function, dependencies and so on
// - module: the shadow module, which is inherited from `mod`

////////////////////////////////////////////////////////////////////////////////////////////////

// Parse an id within an environment, and do range mapping, resolving, applying aliases.
// Returns {Object} parsed object
// @param {string} id
// @param {Object=} env the environment module
function parse_id(id, env) {
  // commonjs parser could not parse non-literal argument of `require`
  id || err('null id');

  env || (env = {});
  // {
  //   alias: {
  //     // id -> path
  //     './a' -> './a.js'
  //   }
  // }
  var map = env.map || {};
  id = map[id] || id;

  // Two kinds of id:
  // - relative module path
  // - package name
  // - a module with path loaded by facade or _use
  var parsed;
  var relative = is_path_relative(id);

  // `env` exists, which means the module is accessed by requiring within another module.
  // `id` is something like '../abc'
  if (relative) {
    env.id || module_not_found(id);

    id = resolve_id(id, env);

    // Legacy
    // If >= 6.2.0, there is always a map,
    // and a value of map is always a top level module id.
    // So, only if it is old wrappings, it would come here.
    // Besides, if not a relative id, we should not adds `'.js'` even it is an old wrapping.
    // How ever, we pass `env` to have a double check.
    // id = legacy_transform_id(id, env);

    parsed = parse_module_id(id);

  // `id` is something like 'jquery'
  } else {
    // 1. id is a package name
    // 'jquery' -> 'jquery@~1.9.3'
    // 2. id may be is a package id
    // 'jquery@^1.9.3' -> 'jquery@^1.9.3'
    id = env.m && env.m[id] || id;
    // 'jquery' -> {n: 'jquery', v: '*', p: ''}
    // 'jquery@~1.9.3' -> {n: 'jquery', v: '~1.9.3', p: ''}
    parsed = parse_module_id(id);
  }

  if (parsed.k === env.k) {
    // if inside the same package of the parent module,
    // it uses a same sub graph of the package
    parsed.graph = env.graph;

  } else {
    // We route a package of certain range to a specific version according to `config.graph`
    // so several modules may point to a same exports
    // if is foreign module, we should parses the graph to the the sub graph
    var sub_graph = get_sub_graph(parsed.k, env.graph)
      // If sub_graph not found, set it as `[]`
      || [];
    parsed.graph = sub_graph;
    parsed.v = sub_graph[0] || parsed.v;
    format_parsed(parsed);
  }

  return parsed;
}

function get_sub_graph (pkg, graph) {
  var global_graph = NEURON_CONF.graph._;
  var deps = graph
    ? graph[1]
    // If `graph` is undefined, fallback to global_graph
    : global_graph;
  return deps && (pkg in deps)
    // `deps[pkg]` is the graph id for the subtle graph
    ? NEURON_CONF.graph[deps[pkg]]
    : global_graph;
}

// Get the exports
// @param {Object} module
function get_exports(module) {
  // Since 6.0.0, neuron will not emit a "cyclic" event.
  // But, detecing static cyclic dependencies is a piece of cake for compilers,
  // such as [cortex](http://github.com/cortexjs/cortex)
  return module.loaded
    ? module.exports

    // #82: since 4.5.0, a module only initialize factory functions when `require()`d.
    : generate_exports(module);
}

// Generate the exports of the module
function generate_exports (module) {
  // # 85
  // Before module factory being invoked, mark the module as `loaded`
  // so we will not execute the factory function again.

  // `mod.loaded` indicates that a module has already been `require()`d
  // When there are cyclic dependencies, neuron will not fail.
  module.loaded = true;

  // During the execution of factory,
  // the reference of `module.exports` might be changed.
  // But we still set the `module.exports` as `{}`,
  // because the module might be `require()`d during the execution of factory
  // if cyclic dependency occurs.
  var exports = module.exports = {};

  // TODO:
  // Calculate `filename` ahead of time
  var __filename
    // = module.filename
    = NEURON_CONF.resolve(module.id);
  var __dirname = dirname(__filename);

  // to keep the object mod away from the executing context of factory,
  // use `factory` instead `mod.factory`,
  // preventing user from fetching runtime data by 'this'
  var factory = module.factory;
  factory(create_require(module), exports, module, __filename, __dirname);
  return module.exports;
}

var guid = 1;

// Get a shadow module or create a new one if not exists
// facade({ entry: 'a' })
function get_module (id, env, strict) {
  var parsed = parse_id(id, env);
  var graph = parsed.graph;
  var mod = get_mod(parsed);

  var real_id = mod.main
    // if is main module, then use `pkg` as `real_id`
    ? parsed.k
    : parsed.id;

  // `graph` is the list of modules for a certain package
  var module = graph[real_id];

  if (!module) {
    !strict || module_not_found(id);
    // So that `module` could be linked with a unique graph
    module = graph[real_id] = create_shadow_module(mod);
    module.graph = graph;

    // guid
    module.g || (module.g = guid ++);
  }

  return module;
}

// @param {Object} module
// @param {function(exports)} callback
function use_module (module, callback) {
  ready(module, function () {
    callback(get_exports(module));
  });
}

// Create a mod
function get_mod(parsed) {
  var id = parsed.id;
  return mods[id] || (mods[id] = {
    // package name: 'a'
    n: parsed.n,
    // package version: '1.1.0'
    v: parsed.v,
    // module path: '/b'
    p: parsed.p,
    // module id: 'a@1.1.0/b'
    id: id,
    // package id: 'a@1.1.0'
    k: parsed.k,
    // version map of the current module
    m: {},
    // loading queue
    l: [],
    // If no path, it must be a main entry.
    // Actually, it actually won't happen when defining a module
    main: !parsed.p
    // map: {Object} The map of aliases to real module id
  });
}

// @param {Object} mod Defined data of mod
function create_shadow_module (mod) {
  function F () {
    // callbacks
    this.r = [];
  }
  F.prototype = mod;
  return new F;
}

// Since 4.2.0, neuron would not allow to require an id with version
// TODO:
// for scoped packages
function test_require_id (id) {
  !~id.indexOf('@') || err("id with '@' is prohibited");
}

// use the sandbox to specify the environment for every id that required in the current module
// @param {Object} env The object of the current module.
// @return {function}
function create_require(env) {
  var require = function(id) {
    // `require('a@0.0.0')` is prohibited.
    test_require_id(id);

    var module = get_module(id, env, true);
    return get_exports(module);
  };

  // @param {string} id Module identifier.
  // Since 4.2.0, we only allow to asynchronously load a single module
  require.async = function(id, callback) {
    var origin = id;
    if (callback) {
      // `require.async('a@0.0.0')` is prohibited
      test_require_id(id);
      var relative = is_path_relative(id);
      if (relative) {
        id = resolve_id(id, env);
        var entries = env.entries;
        id = entries
          ? test_entries(id, entries)
            || test_entries(id + '.js', entries)
            || test_entries(id + '.json', entries)
            || module_not_found(origin)
          // : legacy_transform_id(id, env);
          : id;
      }

      var module = get_module(id, env);
      if (!module.main) {
        if (relative) {
          // If user try to load a non-entry module, it will get a 404 response
          module.a = true;
        } else {
          // We only allow to `require.async` main module or entries of the current package
          return;
        }
      }

      use_module(module, callback);
    }
  };

  // @param {string} path
  // @returns
  // - {string} if valid
  // - otherwise `undefined`
  require.resolve = function (path) {
    // NO, you should not do this:
    // `require.resolve('jquery')`
    // We only allow to resolve a relative path

    // Trying to load the resources of a foreign package is evil.
    if (is_path_relative(path)) {
      path = resolve_path(path, env);

      // If user try to resolve a url outside the current package
      // it fails silently
      if (!~path.indexOf('../')) {
        return NEURON_CONF.resolve(env.k + '/' + path);
      }
    }
  };

  return require;
}

function test_entries (path, entries) {
  return ~entries.indexOf(path)
    ? path
    : FALSE;
}
<commit_msg>#147: improves the logic of require.async<commit_after>// ## Neuron Core: Module Manager
//////////////////////////////////////////////////////////////////////

// ## CommonJS
// Neuron 3.x or newer is not the implementation of any CommonJs proposals
// but only [module/1.0](http://wiki.commonjs.org/wiki/Modules/1.0), one of the stable CommonJs standards.
// And by using neuron and [cortex](http://github.com/cortexjs/cortex), user could write module/1.0 modules.
// Just FORGET `define`.

// ## Naming Conventions of Variables
// All naming of variables should accord to this.

// Take `'a@1.0.0/relative'` for example:

// ### package
// The package which the current module belongs to.
// - name or package name:  {string} package `name`: 'a'
// - package or package id: {string} contains package `name` and `version` and the splitter `'@'`.
//   'a@1.0.0' for instance.

// ### module
// A package is consist of several module objects.
// - mod: {object} the module object. use `mod` instead of `module` to avoid confliction
// - id or module id: the descripter that contains package name, version, and path information
//      {string} for example, `'a@1.0.0/relative'` is a module id(entifier)

// ### version
// Package version: '1.0.0'

// ### main entry
// The module of a package that designed to be accessed from the outside

// ### shadow module and module
// A single module may have different contexts and runtime results
// - mod: the original module definition, including factory function, dependencies and so on
// - module: the shadow module, which is inherited from `mod`

////////////////////////////////////////////////////////////////////////////////////////////////

// Parse an id within an environment, and do range mapping, resolving, applying aliases.
// Returns {Object} parsed object
// @param {string} id
// @param {Object=} env the environment module
function parse_id(id, env) {
  var origin = id;

  // commonjs parser could not parse non-literal argument of `require`
  // So, users might pass a null value into `require()`
  id || err('null id');

  env || (env = {});
  // {
  //   alias: {
  //     // id -> path
  //     './a' -> './a.js'
  //   }
  // }
  var map = env.map || {};
  id = map[id] || id;

  // Two kinds of id:
  // - relative module path
  // - package name
  // - a module with path loaded by `facade` or `neuron._use`
  var parsed;
  var relative = is_path_relative(id);

  // `env` exists,
  // which means the current module is accessed by `require()`ing within another module.
  // `id` is something like '../abc'
  if (relative) {
    env.id || module_not_found(origin);

    id = resolve_id(id, env);
    var entries = env.entries;
    id = entries
      ? test_entries(id, entries)
        || test_entries(id + '.js', entries)
        || test_entries(id + '.json', entries)
        || module_not_found(origin)
      // : legacy_transform_id(id, env);
      : id;

    // Legacy
    // If >= 6.2.0, there is always a map,
    // and a value of map is always a top level module id.
    // So, only if it is old wrappings, it would come here.
    // Besides, if not a relative id, we should not adds `'.js'` even it is an old wrapping.
    // How ever, we pass `env` to have a double check.
    // id = legacy_transform_id(id, env);

    parsed = parse_module_id(id);

  // `id` is something like 'jquery'
  } else {
    // 1. id is a package name
    // 'jquery' -> 'jquery@~1.9.3'
    // 2. id may be is a package id
    // 'jquery@^1.9.3' -> 'jquery@^1.9.3'
    id = env.m && env.m[id] || id;
    // 'jquery' -> {n: 'jquery', v: '*', p: ''}
    // 'jquery@~1.9.3' -> {n: 'jquery', v: '~1.9.3', p: ''}
    parsed = parse_module_id(id);
  }

  if (parsed.k === env.k) {
    // if inside the same package of the parent module,
    // it uses a same sub graph of the package
    parsed.graph = env.graph;

  } else {
    // We route a package of certain range to a specific version according to `config.graph`
    // so several modules may point to a same exports
    // if is foreign module, we should parses the graph to the the sub graph
    var sub_graph = get_sub_graph(parsed.k, env.graph)
      // If sub_graph not found, set it as `[]`
      || [];
    parsed.graph = sub_graph;
    parsed.v = sub_graph[0] || parsed.v;
    format_parsed(parsed);
  }

  return parsed;
}

function get_sub_graph (pkg, graph) {
  var global_graph = NEURON_CONF.graph._;
  var deps = graph
    ? graph[1]
    // If `graph` is undefined, fallback to global_graph
    : global_graph;
  return deps && (pkg in deps)
    // `deps[pkg]` is the graph id for the subtle graph
    ? NEURON_CONF.graph[deps[pkg]]
    : global_graph;
}

// Get the exports
// @param {Object} module
function get_exports(module) {
  // Since 6.0.0, neuron will not emit a "cyclic" event.
  // But, detecing static cyclic dependencies is a piece of cake for compilers,
  // such as [cortex](http://github.com/cortexjs/cortex)
  return module.loaded
    ? module.exports

    // #82: since 4.5.0, a module only initialize factory functions when `require()`d.
    : generate_exports(module);
}

// Generate the exports of the module
function generate_exports (module) {
  // # 85
  // Before module factory being invoked, mark the module as `loaded`
  // so we will not execute the factory function again.

  // `mod.loaded` indicates that a module has already been `require()`d
  // When there are cyclic dependencies, neuron will not fail.
  module.loaded = true;

  // During the execution of factory,
  // the reference of `module.exports` might be changed.
  // But we still set the `module.exports` as `{}`,
  // because the module might be `require()`d during the execution of factory
  // if cyclic dependency occurs.
  var exports = module.exports = {};

  // TODO:
  // Calculate `filename` ahead of time
  var __filename
    // = module.filename
    = NEURON_CONF.resolve(module.id);
  var __dirname = dirname(__filename);

  // to keep the object mod away from the executing context of factory,
  // use `factory` instead `mod.factory`,
  // preventing user from fetching runtime data by 'this'
  var factory = module.factory;
  factory(create_require(module), exports, module, __filename, __dirname);
  return module.exports;
}

var guid = 1;

// Get a shadow module or create a new one if not exists
// facade({ entry: 'a' })
function get_module (id, env, strict) {
  var parsed = parse_id(id, env);
  var graph = parsed.graph;
  var mod = get_mod(parsed);

  var real_id = mod.main
    // if is main module, then use `pkg` as `real_id`
    ? parsed.k
    : parsed.id;

  // `graph` is the list of modules for a certain package
  var module = graph[real_id];

  if (!module) {
    !strict || module_not_found(id);
    // So that `module` could be linked with a unique graph
    module = graph[real_id] = create_shadow_module(mod);
    module.graph = graph;

    // guid
    module.g || (module.g = guid ++);
  }

  return module;
}

// @param {Object} module
// @param {function(exports)} callback
function use_module (module, callback) {
  ready(module, function () {
    callback(get_exports(module));
  });
}

// Create a mod
function get_mod(parsed) {
  var id = parsed.id;
  return mods[id] || (mods[id] = {
    // package name: 'a'
    n: parsed.n,
    // package version: '1.1.0'
    v: parsed.v,
    // module path: '/b'
    p: parsed.p,
    // module id: 'a@1.1.0/b'
    id: id,
    // package id: 'a@1.1.0'
    k: parsed.k,
    // version map of the current module
    m: {},
    // loading queue
    l: [],
    // If no path, it must be a main entry.
    // Actually, it actually won't happen when defining a module
    main: !parsed.p
    // map: {Object} The map of aliases to real module id
  });
}

// @param {Object} mod Defined data of mod
function create_shadow_module (mod) {
  function F () {
    // callbacks
    this.r = [];
  }
  F.prototype = mod;
  return new F;
}

// Since 4.2.0, neuron would not allow to require an id with version
// TODO:
// for scoped packages
function prohibit_require_id_with_version (id) {
  !~id.indexOf('@') || err("id with '@' is prohibited");
}

// use the sandbox to specify the environment for every id that required in the current module
// @param {Object} env The object of the current module.
// @return {function}
function create_require(env) {
  var require = function(id) {
    // `require('a@0.0.0')` is prohibited.
    prohibit_require_id_with_version(id);

    var module = get_module(id, env, true);
    return get_exports(module);
  };

  // @param {string} id Module identifier.
  // Since 4.2.0, we only allow to asynchronously load a single module
  require.async = function(id, callback) {
    if (callback) {
      // `require.async('a@0.0.0')` is prohibited
      prohibit_require_id_with_version(id);
      var module = get_module(id, env);

      // If `require.async` a foreign module, it must be a main entry
      if (!module.main) {

        // Or it should be a module inside the current package
        if (module.n !== env.n) {
          // Otherwise, we will stop that.
          return;
        }
        module.a = true;
      }

      use_module(module, callback);
    }
  };

  // @param {string} path
  // @returns
  // - {string} if valid
  // - otherwise `undefined`
  require.resolve = function (path) {
    // NO, you should not do this:
    // `require.resolve('jquery')`
    // We only allow to resolve a relative path

    // Trying to load the resources of a foreign package is evil.
    if (is_path_relative(path)) {
      path = resolve_path(path, env);

      // If user try to resolve a url outside the current package
      // it fails silently
      if (!~path.indexOf('../')) {
        return NEURON_CONF.resolve(env.k + '/' + path);
      }
    }
  };

  return require;
}

function test_entries (path, entries) {
  return ~entries.indexOf(path)
    ? path
    : FALSE;
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint nomen:true, node:true*/

/**
Main module in the Mojito package that is responsible for creating the mojito
instance to the attached to the `express` app.

Developers should not access this module directly, but instead should
instantiate their app the `express` way.

Usage:

    var app = require('express'),
        mojito = require('mojito'),
        app;

    app = express(); // Mojito instance will be created during that call

    app.use(mojito.middleware()); // register the Mojito specific middleware

    app.post('/tunnel', mojito.tunnelMiddleware()); // setup tunnel dispatcher

    app.mojito.attachRoutes(); // load routes defined in `routes.json`

    // setup other routes here

    app.listen(app.get('port'));

@module mojito
**/

'use strict';

var debug = require('debug')('mojito'),
    express = require('express'),
    libmap = require('express-map'),
    libpath = require('path'),
    appProto = express.application,
    defaultConfiguration = appProto.defaultConfiguration,
    libdispatcher = require('./dispatcher'),
    liblogger = require('./logger'),
    libmiddleware = require('./middleware'),
    librouter = require('./router'),
    libstore = require('./store'),
    libtunnel = require('./tunnel'),
    extend = require('./util').extend,
    Mojito;

//  ----------------------------------------------------------------------------
//  Mojito Global
//  ----------------------------------------------------------------------------

/**
Mojito extension for Express.

@protected
@class Mojito
@constructor
@param {express.application} app `express` application instance
@uses *express, dispatcher, logger, middleware, router, store, tunnel
**/
function Mojito(app) {

    libmap.extend(app);

    this._app = app;
    this._config = {};
    extend(Mojito, libdispatcher, liblogger, libmiddleware, libtunnel);
    app.mojito = {};
    this._init(app);

    return this;
}

/**
Creates a new instance of mojito and attach to `app`.

@method _init
@protected
@param {express.application} app optional

**/
Mojito.prototype._init = function (app) {

    app = app || this._app;

    var context = {},
        appConfig,      // application.json
        options = {},   //
        store;          // reference to resource store

    // debug('applying mojito.defaultConfiguration()');

    if (!app.mojito) {
        throw new Error('`app.mojito` was not initialized correctly.');
    }

    // TODO: use correct static context here
    context.runtime = 'server';

    options = {};
    options.port = process.env.PORT || appConfig.appPort || 8666;
    options.context = context;
    options.mojitoRoot = __dirname;
    // TODO: `root` is required for the resource store, which will be replaced
    // by `locator` at some point. `locator` will have access to the appRoot
    // folder.
    // The application root directory
    options.root = process.cwd();

    appConfig = libstore.getAppConfig(options.root, context);
    options.Y = this._createYUIInstance(options, appConfig);
    liblogger.configureLogger(options.Y);
    store = libstore.createStore(options);
    this._configureAppInstance(app, store, options, appConfig);

    // set port from application.json
    app.set('port', options.port);
};

/**
Configures YUI with both the Mojito framework and all the YUI modules in the
application.

@method _configureYUI
@protected
@param {Object} Y YUI object to configure
@param {Object} store Resource Store which knows what to load
@return {Array} array of YUI module names
**/
Mojito.prototype._configureYUI = function (Y, store) {
    var my = this,
        modules,
        load,
        lang;

    modules = store.yui.getModulesConfig('server', false);
    Y.applyConfig(modules);

    load = Object.keys(modules.modules);

    // NOTE:  Not all of these module names are guaranteed to be valid,
    // but the loader tolerates them anyways.
    for (lang in store.yui.langs) {
        if (store.yui.langs.hasOwnProperty(lang) && lang) {
            load.push('lang/datatype-date-format_' + lang);
        }
    }

    return load;
};

/**
Creates the YUI instance used by the server.

@method _createYUIInstance
@protected
@param {Object} options Same as the `options` parameter to `_configureAppInstance()`.
@param {Object} appConfig The static configuration for the application.
*/
Mojito.prototype._createYUIInstance = function (options, appConfig) {
    var yuiConfig,
        modules = [],
        Y;

    yuiConfig = (appConfig.yui && appConfig.yui.config) || {};

    // redefining "combine" and/or "base" in the server side have side effects
    // and might try to load yui from CDN, so we bypass them.
    // TODO: report bug.
    // is there a better alternative for this delete?
    // maybe not, but it might introduce a perf penalty
    // in v8 engine, and we can't use the undefined trick
    // because loader is doing hasOwnProperty :(
    delete yuiConfig.combine;
    delete yuiConfig.base;

    // in case we want to collect some performance metrics,
    // we can do that by defining the "perf" object in:
    // application.json (master)
    // You can also use the option --perf path/filename.log when
    // running mojito start to dump metrics to disk.
    if (appConfig.perf) {
        yuiConfig.perf = appConfig.perf;
        yuiConfig.perf.logFile = options.perf || yuiConfig.perf.logFile;
    }

    // getting yui module, or yui/debug if needed, and applying
    // the default configuration from application.json->yui-config
    Y = require('yui' + (yuiConfig.filter === 'debug' ? '/debug' : '')).YUI(yuiConfig, {
        useSync: true
    });

    return Y;
};

/**
Adds Mojito framework components to the Express application instance.

@method _configureAppInstance
@protected
@param {express.application} app The Express application instance to Mojito-enable.
@param {ResourceStore} store
@param {Object} options
    @param {Integer} options.port port to listen on
    @param {Object} options.context static context set at startup
    @param {String} options.mojitoRoot
    @param {String} options.root the app root dir
@param {Object} appConfig The static configuration for the application.
**/
Mojito.prototype._configureAppInstance = function (app, store, options, appConfig) {
    var Y = options.Y,
        modules = [];

    modules = this._configureYUI(Y, store);

    // attaching all modules available for this application for the server side
    Y.applyConfig({ useSync: true });
    Y.use.apply(Y, modules);
    Y.applyConfig({ useSync: false });

    // TODO: This might change when `express-yui` and `locator` are integrated
    // and how they are exposed to the `app.mojito` instance.
    //
    // stash for runtime
    extend(app.mojito, {
        store: store,
        Y: Y,
        context: options.context,
        options: options,
        _app: app, // reference to `app` from within `app.mojito`
        attachRoutes: librouter.attachRoutes
    });

    console.log('✔\tMojito ready to serve.');

}; // _configureAppInstance

/**
Hook into `express` default configuration init phase.
**/
appProto.defaultConfiguration = function () {
    defaultConfiguration.apply(this, arguments);

    if (!this.mojito) {
        var mojito = new Mojito(this);
    } else {
        debug('skipping creation of `app.mojito` because it is already defined');
    }
};

//  ----------------------------------------------------------------------------
//  EXPORT(S)
//  ----------------------------------------------------------------------------

/**

Export Mojito extension as a function.

**/

module.exports = Mojito;

<commit_msg>Fix issue on startup where mojito.js trying to use appCofig.appPort before loading calling getAppConfig.<commit_after>/**
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint nomen:true, node:true*/

/**
Main module in the Mojito package that is responsible for creating the mojito
instance to the attached to the `express` app.

Developers should not access this module directly, but instead should
instantiate their app the `express` way.

Usage:

    var app = require('express'),
        mojito = require('mojito'),
        app;

    app = express(); // Mojito instance will be created during that call

    app.use(mojito.middleware()); // register the Mojito specific middleware

    app.post('/tunnel', mojito.tunnelMiddleware()); // setup tunnel dispatcher

    app.mojito.attachRoutes(); // load routes defined in `routes.json`

    // setup other routes here

    app.listen(app.get('port'));

@module mojito
**/

'use strict';

var debug = require('debug')('mojito'),
    express = require('express'),
    libmap = require('express-map'),
    libpath = require('path'),
    appProto = express.application,
    defaultConfiguration = appProto.defaultConfiguration,
    libdispatcher = require('./dispatcher'),
    liblogger = require('./logger'),
    libmiddleware = require('./middleware'),
    librouter = require('./router'),
    libstore = require('./store'),
    libtunnel = require('./tunnel'),
    extend = require('./util').extend,
    Mojito;

//  ----------------------------------------------------------------------------
//  Mojito Global
//  ----------------------------------------------------------------------------

/**
Mojito extension for Express.

@protected
@class Mojito
@constructor
@param {express.application} app `express` application instance
@uses *express, dispatcher, logger, middleware, router, store, tunnel
**/
function Mojito(app) {

    libmap.extend(app);

    this._app = app;
    this._config = {};
    extend(Mojito, libdispatcher, liblogger, libmiddleware, libtunnel);
    app.mojito = {};
    this._init(app);

    return this;
}

/**
Creates a new instance of mojito and attach to `app`.

@method _init
@protected
@param {express.application} app optional

**/
Mojito.prototype._init = function (app) {

    app = app || this._app;

    var context = {},
        appConfig,      // application.json
        appRoot = process.cwd(),
        options = {},   //
        store;          // reference to resource store

    // debug('applying mojito.defaultConfiguration()');

    if (!app.mojito) {
        throw new Error('`app.mojito` was not initialized correctly.');
    }

    // TODO: use correct static context here
    context.runtime = 'server';
    appConfig = libstore.getAppConfig(appRoot, context);

    options = {};
    options.port = process.env.PORT || appConfig.appPort || 8666;
    options.context = context;
    options.mojitoRoot = __dirname;
    // TODO: `root` is required for the resource store, which will be replaced
    // by `locator` at some point. `locator` will have access to the appRoot
    // folder.
    // The application root directory
    options.root = appRoot;

    options.Y = this._createYUIInstance(options, appConfig);
    liblogger.configureLogger(options.Y);
    store = libstore.createStore(options);
    this._configureAppInstance(app, store, options, appConfig);

    // set port from application.json
    app.set('port', options.port);
};

/**
Configures YUI with both the Mojito framework and all the YUI modules in the
application.

@method _configureYUI
@protected
@param {Object} Y YUI object to configure
@param {Object} store Resource Store which knows what to load
@return {Array} array of YUI module names
**/
Mojito.prototype._configureYUI = function (Y, store) {
    var my = this,
        modules,
        load,
        lang;

    modules = store.yui.getModulesConfig('server', false);
    Y.applyConfig(modules);

    load = Object.keys(modules.modules);

    // NOTE:  Not all of these module names are guaranteed to be valid,
    // but the loader tolerates them anyways.
    for (lang in store.yui.langs) {
        if (store.yui.langs.hasOwnProperty(lang) && lang) {
            load.push('lang/datatype-date-format_' + lang);
        }
    }

    return load;
};

/**
Creates the YUI instance used by the server.

@method _createYUIInstance
@protected
@param {Object} options Same as the `options` parameter to `_configureAppInstance()`.
@param {Object} appConfig The static configuration for the application.
*/
Mojito.prototype._createYUIInstance = function (options, appConfig) {
    var yuiConfig,
        modules = [],
        Y;

    yuiConfig = (appConfig.yui && appConfig.yui.config) || {};

    // redefining "combine" and/or "base" in the server side have side effects
    // and might try to load yui from CDN, so we bypass them.
    // TODO: report bug.
    // is there a better alternative for this delete?
    // maybe not, but it might introduce a perf penalty
    // in v8 engine, and we can't use the undefined trick
    // because loader is doing hasOwnProperty :(
    delete yuiConfig.combine;
    delete yuiConfig.base;

    // in case we want to collect some performance metrics,
    // we can do that by defining the "perf" object in:
    // application.json (master)
    // You can also use the option --perf path/filename.log when
    // running mojito start to dump metrics to disk.
    if (appConfig.perf) {
        yuiConfig.perf = appConfig.perf;
        yuiConfig.perf.logFile = options.perf || yuiConfig.perf.logFile;
    }

    // getting yui module, or yui/debug if needed, and applying
    // the default configuration from application.json->yui-config
    Y = require('yui' + (yuiConfig.filter === 'debug' ? '/debug' : '')).YUI(yuiConfig, {
        useSync: true
    });

    return Y;
};

/**
Adds Mojito framework components to the Express application instance.

@method _configureAppInstance
@protected
@param {express.application} app The Express application instance to Mojito-enable.
@param {ResourceStore} store
@param {Object} options
    @param {Integer} options.port port to listen on
    @param {Object} options.context static context set at startup
    @param {String} options.mojitoRoot
    @param {String} options.root the app root dir
@param {Object} appConfig The static configuration for the application.
**/
Mojito.prototype._configureAppInstance = function (app, store, options, appConfig) {
    var Y = options.Y,
        modules = [];

    modules = this._configureYUI(Y, store);

    // attaching all modules available for this application for the server side
    Y.applyConfig({ useSync: true });
    Y.use.apply(Y, modules);
    Y.applyConfig({ useSync: false });

    // TODO: This might change when `express-yui` and `locator` are integrated
    // and how they are exposed to the `app.mojito` instance.
    //
    // stash for runtime
    extend(app.mojito, {
        store: store,
        Y: Y,
        context: options.context,
        options: options,
        _app: app, // reference to `app` from within `app.mojito`
        attachRoutes: librouter.attachRoutes
    });

    console.log('✔\tMojito ready to serve.');

}; // _configureAppInstance

/**
Hook into `express` default configuration init phase.
**/
appProto.defaultConfiguration = function () {
    defaultConfiguration.apply(this, arguments);

    if (!this.mojito) {
        var mojito = new Mojito(this);
    } else {
        debug('skipping creation of `app.mojito` because it is already defined');
    }
};

//  ----------------------------------------------------------------------------
//  EXPORT(S)
//  ----------------------------------------------------------------------------

/**

Export Mojito extension as a function.

**/

module.exports = Mojito;

<|endoftext|>