<commit_before>// http://www.quirksmode.org/js/keys.html
/**
 * atta(b completion)
 * usage:
 *   $(selector).atta({
 *      completions: function () { return ['Foo', 'Bar', 'Baz' ] }
 *   })
 */
function ($) {
  'use strict';
  var defaults = {
      completions: function() {
          return $("#rsvp-list a.mem-name").
              map(function (i, a) { return a.text; }).sort();
      },
      at : {
          is: function(e){
              return  String.fromCharCode(e.which || e.keyCode) === "2" && e.shiftKey
          }
          , char : '@'
      }
  };
  /**
   * options is an object which may contain one of:
   * completions - a function which returns a list of completion options
   * at - an object representing the at trigger for tab completion.
   *      this object should contain a function, 'is', which should accept a key event
   *      and return true if the key event matches the trigger
   *      and a 'char' property which represents the display of the trigger.
   */
  $.fn.atta = function(options) {
     var self = this
      , options = options || {}
      , keys = {
          'up': 38
          , 'down': 40
          , 'escape': 27
          , 'enter': 13
          , 'tab': 9
      }
      , styles = [
          '#atta-list-container { position:relative; top: 0px; left: 5px; }'
          , '#atta-list { position:relative;margin-top:18px;background:#fff;border-radius:3px;border:1px solid #ddd;box-shadow:0 0 5px rgba(0,0,0,0.1);min-width:180px;}'
          , '#atta-list ul { list-style:none;margin:0;padding:0; }'
          , '#atta-list li { padding:5px 10px;font-weight:bold;border-bottom:1px solid #eee; color:#333; }'
          , '#atta-list li.sel { background:#1DCAFF; color:#fff; }' ]
      , completions = options.completions || defaults.completions
      , at = options.at || defaults.at
      , newList = function () {
          return $("

");
      }
      , newItem = function (name, i) {
          return '
' +
              name + '
';
      }
      , key = function(e, alias) {
          return (e.which || e.keyCode) === keys[alias];
      }
      , cancelKey = function (e) { return key(e, 'escape'); }
      , acceptKey = function (e) { return key(e, 'enter') || key(e, 'tab'); }
      , upKey = function (e) { return key(e, 'up'); }
      , downKey = function (e) { return key(e, 'down'); }
      , tabKey = function (e) { return key(e, 'tab'); }
      , buildMarkup = function (cs) {
          var current = $("#atta-list-container")
          , markup = current.length > 0 && current || newList()
          , ul = markup.find('ul')
          , buff = [];
          for(var i = 0, l = cs.length;i < l;i++) {
              buff.push(newItem(cs[i], i));
          }
          ul.empty().append(buff.join(''));
          return markup;
      }
      , cancel = function() {
          $('#atta-list-container').remove();
      }
      , showCompletions = function (ta, cs) {
          var markup = buildMarkup(cs);
          $(window).on('keyup', function (e) {
              var sel = $("#atta-list li.sel")
              , kids = $(sel.parent()).children()
              , cnt = kids.length
              , index = sel.length > 0 && sel.data().index || 0;
              if (cnt) {
                  if (upKey(e)) {
                      e.preventDefault();
                      sel.removeClass('sel');
                      if (index > 0) {
                          $(kids[index-1]).addClass('sel')
                      } else if (index == 0) {
                          $(kids[cnt-1]).addClass('sel')
                      }
                      return false;
                  } else if (downKey(e)) {
                      e.preventDefault();
                      sel.removeClass('sel');
                      if (index < cnt-1) {
                          $(kids[index+1]).addClass('sel')
                      } else if (index == cnt-1) {
                          $(kids[0]).addClass('sel')
                      }
                      return false;
                  }
              }
          });
          $(ta).parent().append(markup);
      };

      // append atta styles only once
      if ($('#atta-style').length < 1) {
          $('head').append('')
      }

      // bind accept and cancelation listener
      $(window).on('keyup', function(e) {
          if (cancelKey(e)) {
              cancel();
          } else if (acceptKey(e)) {
              var container = $('#atta-list-container');
              if (container.length > 0) {
                  e.preventDefault();
                  var focused = $("*:focus");
                  console.log(focused);
                  var name = container.find('.sel').text();
                  console.log('select ' + name);
                  cancel();
                  return false;
              }
          }
      });

      // bind query listener
      return self.each(function() {
          $(this).live('keyup', function (e) {
              var el = $(e.target);
              if (at.is(e)) {
                  showCompletions(e.target, completions() || []);
              } else {
                  var container = $("#atta-list-container");
                  if (container.length > 0) {
                      var txt = el.val(), query, matching, filteredCompletions;
                      if (txt.length > 0) {
                          var lastAt = txt.lastIndexOf(at.char);
                          if (lastAt != -1) {
                              query = txt.slice(lastAt + 1, txt.length);
                              matching = function(i, e) {
                                  return e.toLowerCase().indexOf(query.toLowerCase()) != -1;
                              }
                              filteredCompletions = (completions() || []).filter(matching);
                              showCompletions(el, filteredCompletions);
                          }
                      }
                  }
              }
          });
      });
  };
)(jQuery);<commit_msg>bind navigation once<commit_after>// http://www.quirksmode.org/js/keys.html
/**
 * atta(b completion)
 * usage:
 *   $(selector).atta({
 *      completions: function () { return ['Foo', 'Bar', 'Baz' ] }
 *   })
 */
function ($) {
  'use strict';
  var defaults = {
      completions: function() {
          return $("#rsvp-list a.mem-name").
              map(function (i, a) { return a.text; }).sort();
      },
      at : {
          is: function(e){
              return  String.fromCharCode(e.which || e.keyCode) === "2" && e.shiftKey
          }
          , char : '@'
      }
  };
  /**
   * options is an object which may contain one of:
   * completions - a function which returns a list of completion options
   * at - an object representing the at trigger for tab completion.
   *      this object should contain a function, 'is', which should accept a key event
   *      and return true if the key event matches the trigger
   *      and a 'char' property which represents the display of the trigger.
   */
  $.fn.atta = function(options) {
     var self = this
      , options = options || {}
      , keys = {
          'up': 38
          , 'down': 40
          , 'escape': 27
          , 'enter': 13
          , 'tab': 9
      }
      , styles = [
          '#atta-list-container { position:relative; top: 0px; left: 5px; }'
          , '#atta-list { position:relative;margin-top:18px;background:#fff;border-radius:3px;border:1px solid #ddd;box-shadow:0 0 5px rgba(0,0,0,0.1);min-width:180px;}'
          , '#atta-list ul { list-style:none;margin:0;padding:0; }'
          , '#atta-list li { padding:5px 10px;font-weight:bold;border-bottom:1px solid #eee; color:#333; }'
          , '#atta-list li.sel { background:#1DCAFF; color:#fff; }' ]
      , completions = options.completions || defaults.completions
      , at = options.at || defaults.at
      , newList = function () {
          return $("

");
      }
      , newItem = function (name, i) {
          return '
' +
              name + '
';
      }
      , key = function(e, alias) {
          return (e.which || e.keyCode) === keys[alias];
      }
      , cancelKey = function (e) { return key(e, 'escape'); }
      , acceptKey = function (e) { return key(e, 'enter') || key(e, 'tab'); }
      , upKey = function (e) { return key(e, 'up'); }
      , downKey = function (e) { return key(e, 'down'); }
      , tabKey = function (e) { return key(e, 'tab'); }
      , buildMarkup = function (cs) {
          var current = $("#atta-list-container")
          , markup = current.length > 0 && current || newList()
          , ul = markup.find('ul')
          , buff = [];
          for(var i = 0, l = cs.length;i < l;i++) {
              buff.push(newItem(cs[i], i));
          }
          ul.empty().append(buff.join(''));
          return markup;
      }
      , cancel = function() {
          $('#atta-list-container').remove();
      }
      , showCompletions = function (ta, cs) {
          var markup = buildMarkup(cs);
          $(ta).parent().append(markup);
      }
      , navigationBound = false // todo better way to detect this
      , bindNavigation = function() {
          navigationBound = true
          $(window).on('keyup', function (e) {
              var sel = $("#atta-list li.sel")
              , kids = $(sel.parent()).children()
              , cnt = kids.length
              , index = sel.length > 0 && sel.data().index || 0;
              if (cnt) {
                  if (upKey(e)) {
                      e.preventDefault();
                      sel.removeClass('sel');
                      if (index > 0) {
                          $(kids[index-1]).addClass('sel')
                      } else if (index == 0) {
                          $(kids[cnt-1]).addClass('sel')
                      }
                      return false;
                  } else if (downKey(e)) {
                      e.preventDefault();
                      sel.removeClass('sel');
                      if (index < cnt-1) {
                          $(kids[index+1]).addClass('sel')
                      } else if (index == cnt-1) {
                          $(kids[0]).addClass('sel')
                      }
                      return false;
                  }
              }
          });
      };

      // append atta styles only once
      if ($('#atta-style').length < 1) {
          $('head').append('')
      }

      // bind accept and cancelation listener
      $(window).on('keyup', function(e) {
          if (cancelKey(e)) {
              cancel();
          } else if (acceptKey(e)) {
              var container = $('#atta-list-container');
              if (container.length > 0) {
                  e.preventDefault();
                  var focused = $("*:focus");
                  console.log(focused);
                  var name = container.find('.sel').text();
                  console.log('select ' + name);
                  cancel();
                  return false;
              }
          }
      });

      // bind query listener
      return self.each(function() {
          $(this).live('keyup', function (e) {
              var el = $(e.target);
              if (at.is(e)) {
                  showCompletions(e.target, completions() || []);
                  if (!navigationBound) {
                      bindNavigation();
                  }
              } else {
                  var container = $("#atta-list-container");
                  if (container.length > 0) {
                      var txt = el.val(), query, matching, filteredCompletions;
                      if (txt.length > 0) {
                          var lastAt = txt.lastIndexOf(at.char);
                          if (lastAt != -1) {
                              query = txt.slice(lastAt + 1, txt.length);
                              matching = function(i, e) {
                                  return e.toLowerCase().indexOf(query.toLowerCase()) != -1;
                              }
                              filteredCompletions = (completions() || []).filter(matching);
                              showCompletions(el, filteredCompletions);
                          }
                      }
                  }
              }
          });
      });
  };
)(jQuery);<|endoftext|>


<commit_before>(function() {
  uR.auth = uR.auth || {};
  uR.auth.loginRequired = function loginRequired(func,data) {
    if (typeof func == "string") {
      var tagname = func;
      func = function(path,data) {
        uR.mountElement(tagname,data)
      }
    }
    data = data || {};
    function wrapped() {
      var args = arguments;
      function success(data) {
        if (data) { uR.auth.setUser(data.user); }
        func.apply(this,args);
      }
      if (!uR.auth.user || data.force) {
        uR.AUTH_SUCCESS = success;
        data.slug = "register";
        uR.alertElement("auth-modal",data);
      }
      else { success(); }
    }
    wrapped.login_required = true;
    return wrapped;
  }
  uR.auth.setUser = function setUser(user) {
    uR.storage.set('auth.user',user || null); // JSON.stringify hates undefined
    uR.auth.user = user;
    riot.update(uR.auth.tag_names);
  }
  uR.auth._getLinks = function() {
    return [
      {url: "/account/settings/", icon: "gear", text: "Account Settings"},
      {url: "/auth/logout/", icon: "sign-out", text: "Log Out"},
    ];
  }
  uR.auth.getLinks = uR.auth._getLinks;

  uR.schema.auth = {
    login: [
      { name: 'username', label: 'Username or Email' },
      { name: 'password', type: 'password' },
    ],
    register: [
      { name: 'email', label: 'Email Address', type: "email" },
      { name: 'password', type: 'password' },
    ],
    'password-reset': [ { name: 'email', label: 'Email Address', type: "email" }, ]
  };
  uR.urls.auth = {
    login: "/auth/login/"
  }
  uR.urls.api = uR.urls.api || {};
  uR.urls.api.login = "/api/login/";
  uR.urls.api.register = "/api/register/";
  uR.urls.api['password-reset'] = "/api/password-reset/";
  uR.auth.tag_names = 'auth-dropdown';
  uR.addRoutes({
    "/auth/(login|register|forgot-password)/": function(path,data) { uR.alertElement("auth-modal",data); },
    "/auth/logout/": function(path,data) {
      uR.auth.setUser(null);
      window.location = "/accounts/logout/";
    },
  });

  uR.auth.user = uR.storage.get("auth.user");
  uR.ready(function() {
    riot.mount(uR.auth.tag_names);
    uR.ajax({
      url: "/user.json",
      success: function(data) {
        if (data.user != uR.auth.user) { uR.auth.setUser(data.user); }
      },
    });
  });
})()
<commit_msg>postAuth<commit_after>(function() {
  uR.auth = uR.auth || {};
  uR.auth.loginRequired = function loginRequired(func,data) {
    if (typeof func == "string") {
      var tagname = func;
      func = function(path,data) {
        uR.mountElement(tagname,data)
      }
    }
    data = data || {};
    function wrapped() {
      var args = arguments;
      function success(data) {
        if (data) { uR.auth.setUser(data.user); }
        func.apply(this,args);
      }
      if (!uR.auth.user || data.force) {
        uR.AUTH_SUCCESS = success;
        data.slug = "register";
        uR.alertElement("auth-modal",data);
      }
      else { success(); }
    }
    wrapped.login_required = true;
    return wrapped;
  }
  uR.auth.setUser = function setUser(user) {
    uR.storage.set('auth.user',user || null); // JSON.stringify hates undefined
    uR.auth.user = user;
    uR.auth.postAuth();
    riot.update(uR.auth.tag_names);
  }
  uR.auth.postAuth = function() {}
  uR.auth._getLinks = function() {
    return [
      {url: "/account/settings/", icon: "gear", text: "Account Settings"},
      {url: "/auth/logout/", icon: "sign-out", text: "Log Out"},
    ];
  }
  uR.auth.getLinks = uR.auth._getLinks;

  uR.schema.auth = {
    login: [
      { name: 'username', label: 'Username or Email' },
      { name: 'password', type: 'password' },
    ],
    register: [
      { name: 'email', label: 'Email Address', type: "email" },
      { name: 'password', type: 'password' },
    ],
    'password-reset': [ { name: 'email', label: 'Email Address', type: "email" }, ]
  };
  uR.urls.auth = {
    login: "/auth/login/"
  }
  uR.urls.api = uR.urls.api || {};
  uR.urls.api.login = "/api/login/";
  uR.urls.api.register = "/api/register/";
  uR.urls.api['password-reset'] = "/api/password-reset/";
  uR.auth.tag_names = 'auth-dropdown';
  uR.addRoutes({
    "/auth/(login|register|forgot-password)/": function(path,data) { uR.alertElement("auth-modal",data); },
    "/auth/logout/": function(path,data) {
      uR.auth.setUser(null);
      window.location = "/accounts/logout/";
    },
  });

  uR.auth.user = uR.storage.get("auth.user");
  uR.ready(function() {
    riot.mount(uR.auth.tag_names);
    uR.auth.reset();
  });
  uR.auth.reset = function(callback) {
    callback = callback || function() {}
    uR.ajax({
      url: "/user.json",
      success: function(data) {
        if (data.user != uR.auth.user) { uR.auth.setUser(data.user); }
        callback()
      },
    });
  }
})()
<|endoftext|>


<commit_before>'use strict';

var mcf = require('minecraft-protocol-forge');
var mc = require('minecraft-protocol');
var ping = mc.ping;
var assert = require('assert');

function createClientAuto(options, cb) {
  assert.ok(options, 'options is required');

  console.log('pinging',options.host);
  // TODO: refactor with DNS SRV lookup in NMP
  // TODO: detect ping timeout, https://github.com/PrismarineJS/node-minecraft-protocol/issues/329
  ping(options, function(err, response) {
    var client;

    if (err) return cb(err, null);
    console.log('ping response',response);
    // TODO: could also use ping pre-connect to save description, type, negotiate protocol etc.
    //  ^ see https://github.com/PrismarineJS/node-minecraft-protocol/issues/327
    var motd = response.description;
    console.log('Server description:',motd); // TODO: save

    // Pass server-reported version to protocol handler
    // The version string is interpereted by https://github.com/PrismarineJS/node-minecraft-data
    var versionName = response.version.name;        // 1.8.9, 1.7.10
    var versionProtocol = response.version.protocol;//    47,      5

    console.log(`Server version: ${versionName}, protocol: ${versionProtocol}`);
    // TODO: test or report unsupported versions?
    // TODO: fix non-vanilla/FML server support! Spigot 1.8.8, Glowstone++ 1.8.9 is 'versionName'; should use versionProtocol
    //

    options.version = versionName;

    if (response.modinfo && response.modinfo.type === 'FML') {
      // Use the list of Forge mods from the server ping, so client will match server
      var forgeMods = response.modinfo.modList;
      console.log('Using forgeMods:',forgeMods);
      client = mcf.createClient(options); // we know we can use Forge
      client.forgeMods = forgeMods; // for fmlHandshakeStep TODO: refactor, constructor?
    } else {
        client = mc.createClient(options); // vanilla
    }
    cb(null, client);
  });
}

module.exports = {
  createClientAsync: createClientAuto
};
<commit_msg>Use protocol version instead of name; support Glowstone++/Spigot<commit_after>'use strict';

var mcf = require('minecraft-protocol-forge');
var mc = require('minecraft-protocol');
var ping = mc.ping;
var assert = require('assert');

// TODO: move into minecraft-data? is it already there?
var protocol2version = {
  '47': '1.8.9',
  '5': '1.7.10'
};

function createClientAuto(options, cb) {
  assert.ok(options, 'options is required');

  console.log('pinging',options.host);
  // TODO: refactor with DNS SRV lookup in NMP
  // TODO: detect ping timeout, https://github.com/PrismarineJS/node-minecraft-protocol/issues/329
  ping(options, function(err, response) {
    var client;

    if (err) return cb(err, null);
    console.log('ping response',response);
    // TODO: could also use ping pre-connect to save description, type, negotiate protocol etc.
    //  ^ see https://github.com/PrismarineJS/node-minecraft-protocol/issues/327
    var motd = response.description;
    console.log('Server description:',motd); // TODO: save

    // Pass server-reported version to protocol handler
    // The version string is interpereted by https://github.com/PrismarineJS/node-minecraft-data
    var versionName = response.version.name;        // 1.8.9, 1.7.10
    var versionProtocol = response.version.protocol;//    47,      5

    console.log(`Server version: ${versionName}, protocol: ${versionProtocol}`);
    // Note that versionName is a descriptive version stirng like '1.8.9' on vailla, but other
    // servers add their own name (Spigot 1.8.8, Glowstone++ 1.8.9) so we cannot use it directly,
    // even though it is in a format accepted by minecraft-data. Instead, translate the protocol.
    options.version = protocol2version[versionProtocol];
    if (!options.version) {
      throw new Error(`unsupported/unknown protocol version: ${versionProtocol}, update protocol2Version`);
    }

    if (response.modinfo && response.modinfo.type === 'FML') {
      // Use the list of Forge mods from the server ping, so client will match server
      var forgeMods = response.modinfo.modList;
      console.log('Using forgeMods:',forgeMods);
      client = mcf.createClient(options); // we know we can use Forge
      client.forgeMods = forgeMods; // for fmlHandshakeStep TODO: refactor, constructor?
    } else {
        client = mc.createClient(options); // vanilla
    }
    cb(null, client);
  });
}

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


<commit_before>var aws4 = exports,
    url = require('url'),
    querystring = require('querystring'),
    crypto = require('crypto'),
    lru = require('./lru'),
    credentialsCache = lru(1000)

// http://docs.amazonwebservices.com/general/latest/gr/signature-version-4.html

function hmac(key, string, encoding) {
  return crypto.createHmac('sha256', key).update(string, 'utf8').digest(encoding)
}

function hash(string, encoding) {
  return crypto.createHash('sha256').update(string, 'utf8').digest(encoding)
}

// This function assumes the string has already been percent encoded
function encodeRfc3986(urlEncodedString) {
  return urlEncodedString.replace(/[!'()*]/g, function(c) {
    return '%' + c.charCodeAt(0).toString(16).toUpperCase()
  })
}

// request: { path | body, [host], [method], [headers], [service], [region] }
// credentials: { accessKeyId, secretAccessKey, [sessionToken] }
function RequestSigner(request, credentials) {

  if (typeof request === 'string') request = url.parse(request)

  var headers = request.headers = (request.headers || {}),
      hostParts = this.matchHost(request.hostname || request.host || headers.Host || headers.host)

  this.request = request
  this.credentials = credentials || this.defaultCredentials()

  this.service = request.service || hostParts[0] || ''
  this.region = request.region || hostParts[1] || 'us-east-1'

  // SES uses a different domain from the service name
  if (this.service === 'email') this.service = 'ses'

  if (!request.method && request.body)
    request.method = 'POST'

  if (!headers.Host && !headers.host) {
    headers.Host = request.hostname || request.host || this.createHost()

    // If a port is specified explicitly, use it as is
    if (request.port)
      headers.Host += ':' + request.port
  }
  if (!request.hostname && !request.host)
    request.hostname = headers.Host || headers.host

  this.isCodeCommitGit = this.service === 'codecommit' && request.method === 'GIT'
}

RequestSigner.prototype.matchHost = function(host) {
  var match = (host || '').match(/([^\.]+)\.(?:([^\.]*)\.)?amazonaws\.com$/)
  var hostParts = (match || []).slice(1, 3)

  // ES's hostParts are sometimes the other way round, if the value that is expected
  // to be region equals ‘es’ switch them back
  // e.g. search-cluster-name-aaaa00aaaa0aaa0aaaaaaa0aaa.us-east-1.es.amazonaws.com
  if (hostParts[1] === 'es')
    hostParts = hostParts.reverse()

  return hostParts
}

// http://docs.aws.amazon.com/general/latest/gr/rande.html
RequestSigner.prototype.isSingleRegion = function() {
  // Special case for S3 and SimpleDB in us-east-1
  if (['s3', 'sdb'].indexOf(this.service) >= 0 && this.region === 'us-east-1') return true

  return ['cloudfront', 'ls', 'route53', 'iam', 'importexport', 'sts']
    .indexOf(this.service) >= 0
}

RequestSigner.prototype.createHost = function() {
  var region = this.isSingleRegion() ? '' :
        (this.service === 's3' && this.region !== 'us-east-1' ? '-' : '.') + this.region,
      service = this.service === 'ses' ? 'email' : this.service
  return service + region + '.amazonaws.com'
}

RequestSigner.prototype.prepareRequest = function() {
  this.parsePath()

  var request = this.request, headers = request.headers, query

  if (request.signQuery) {

    this.parsedPath.query = query = this.parsedPath.query || {}

    if (this.credentials.sessionToken)
      query['X-Amz-Security-Token'] = this.credentials.sessionToken

    if (this.service === 's3' && !query['X-Amz-Expires'])
      query['X-Amz-Expires'] = 86400

    if (query['X-Amz-Date'])
      this.datetime = query['X-Amz-Date']
    else
      query['X-Amz-Date'] = this.getDateTime()

    query['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256'
    query['X-Amz-Credential'] = this.credentials.accessKeyId + '/' + this.credentialString()
    query['X-Amz-SignedHeaders'] = this.signedHeaders()

  } else {

    if (!request.doNotModifyHeaders && !this.isCodeCommitGit) {
      if (request.body && !headers['Content-Type'] && !headers['content-type'])
        headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'

      if (request.body && !headers['Content-Length'] && !headers['content-length'])
        headers['Content-Length'] = Buffer.byteLength(request.body)

      if (this.credentials.sessionToken)
        headers['X-Amz-Security-Token'] = this.credentials.sessionToken

      if (this.service === 's3')
        headers['X-Amz-Content-Sha256'] = hash(this.request.body || '', 'hex')

      if (headers['X-Amz-Date'])
        this.datetime = headers['X-Amz-Date']
      else
        headers['X-Amz-Date'] = this.getDateTime()
    }

    delete headers.Authorization
    delete headers.authorization
  }
}

RequestSigner.prototype.sign = function() {
  if (!this.parsedPath) this.prepareRequest()

  if (this.request.signQuery) {
    this.parsedPath.query['X-Amz-Signature'] = this.signature()
  } else {
    this.request.headers.Authorization = this.authHeader()
  }

  this.request.path = this.formatPath()

  return this.request
}

RequestSigner.prototype.getDateTime = function() {
  if (!this.datetime) {
    var headers = this.request.headers,
      date = new Date(headers.Date || headers.date || new Date)

    this.datetime = date.toISOString().replace(/[:\-]|\.\d{3}/g, '')

    // Remove the trailing 'Z' on the timestamp string for CodeCommit git access
    if (this.isCodeCommitGit) this.datetime = this.datetime.slice(0, -1)
  }
  return this.datetime
}

RequestSigner.prototype.getDate = function() {
  return this.getDateTime().substr(0, 8)
}

RequestSigner.prototype.authHeader = function() {
  return [
    'AWS4-HMAC-SHA256 Credential=' + this.credentials.accessKeyId + '/' + this.credentialString(),
    'SignedHeaders=' + this.signedHeaders(),
    'Signature=' + this.signature(),
  ].join(', ')
}

RequestSigner.prototype.signature = function() {
  var date = this.getDate(),
      cacheKey = [this.credentials.secretAccessKey, date, this.region, this.service].join(),
      kDate, kRegion, kService, kCredentials = credentialsCache.get(cacheKey)
  if (!kCredentials) {
    kDate = hmac('AWS4' + this.credentials.secretAccessKey, date)
    kRegion = hmac(kDate, this.region)
    kService = hmac(kRegion, this.service)
    kCredentials = hmac(kService, 'aws4_request')
    credentialsCache.set(cacheKey, kCredentials)
  }
  return hmac(kCredentials, this.stringToSign(), 'hex')
}

RequestSigner.prototype.stringToSign = function() {
  return [
    'AWS4-HMAC-SHA256',
    this.getDateTime(),
    this.credentialString(),
    hash(this.canonicalString(), 'hex'),
  ].join('\n')
}

RequestSigner.prototype.canonicalString = function() {
  if (!this.parsedPath) this.prepareRequest()

  var pathStr = this.parsedPath.path,
      query = this.parsedPath.query,
      queryStr = '',
      normalizePath = this.service !== 's3',
      decodePath = this.service === 's3' || this.request.doNotEncodePath,
      decodeSlashesInPath = this.service === 's3',
      firstValOnly = this.service === 's3',
      bodyHash = this.service === 's3' && this.request.signQuery ? 'UNSIGNED-PAYLOAD' :
        (this.isCodeCommitGit ? '' : hash(this.request.body || '', 'hex'))

  if (query) {
    queryStr = encodeRfc3986(querystring.stringify(Object.keys(query).sort().reduce(function(obj, key) {
      if (!key) return obj
      obj[key] = !Array.isArray(query[key]) ? query[key] :
        (firstValOnly ? query[key][0] : query[key].slice().sort())
      return obj
    }, {})))
  }
  if (pathStr !== '/') {
    if (normalizePath) pathStr = pathStr.replace(/\/{2,}/g, '/')
    pathStr = pathStr.split('/').reduce(function(path, piece) {
      if (normalizePath && piece === '..') {
        path.pop()
      } else if (!normalizePath || piece !== '.') {
        if (decodePath) piece = querystring.unescape(piece)
        path.push(encodeRfc3986(querystring.escape(piece)))
      }
      return path
    }, []).join('/')
    if (pathStr[0] !== '/') pathStr = '/' + pathStr
    if (decodeSlashesInPath) pathStr = pathStr.replace(/%2F/g, '/')
  }

  return [
    this.request.method || 'GET',
    pathStr,
    queryStr,
    this.canonicalHeaders() + '\n',
    this.signedHeaders(),
    bodyHash,
  ].join('\n')
}

RequestSigner.prototype.canonicalHeaders = function() {
  var headers = this.request.headers
  function trimAll(header) {
    return header.toString().trim().replace(/\s+/g, ' ')
  }
  return Object.keys(headers)
    .sort(function(a, b) { return a.toLowerCase() < b.toLowerCase() ? -1 : 1 })
    .map(function(key) { return key.toLowerCase() + ':' + trimAll(headers[key]) })
    .join('\n')
}

RequestSigner.prototype.signedHeaders = function() {
  return Object.keys(this.request.headers)
    .map(function(key) { return key.toLowerCase() })
    .sort()
    .join(';')
}

RequestSigner.prototype.credentialString = function() {
  return [
    this.getDate(),
    this.region,
    this.service,
    'aws4_request',
  ].join('/')
}

RequestSigner.prototype.defaultCredentials = function() {
  var env = process.env
  return {
    accessKeyId: env.AWS_ACCESS_KEY_ID || env.AWS_ACCESS_KEY,
    secretAccessKey: env.AWS_SECRET_ACCESS_KEY || env.AWS_SECRET_KEY,
    sessionToken: env.AWS_SESSION_TOKEN,
  }
}

RequestSigner.prototype.parsePath = function() {
  var path = this.request.path || '/',
      queryIx = path.indexOf('?'),
      query = null

  if (queryIx >= 0) {
    query = querystring.parse(path.slice(queryIx + 1))
    path = path.slice(0, queryIx)
  }

  // S3 doesn't always encode characters > 127 correctly and
  // all services don't encode characters > 255 correctly
  // So if there are non-reserved chars (and it's not already all % encoded), just encode them all
  if (/[^0-9A-Za-z!'()*\-._~%/]/.test(path)) {
    path = path.split('/').map(function(piece) {
      return querystring.escape(querystring.unescape(piece))
    }).join('/')
  }

  this.parsedPath = {
    path: path,
    query: query,
  }
}

RequestSigner.prototype.formatPath = function() {
  var path = this.parsedPath.path,
      query = this.parsedPath.query

  if (!query) return path

  // Services don't support empty query string keys
  if (query[''] != null) delete query['']

  return path + '?' + encodeRfc3986(querystring.stringify(query))
}

aws4.RequestSigner = RequestSigner

aws4.sign = function(request, credentials) {
  return new RequestSigner(request, credentials).sign()
}
<commit_msg>Allow pre-computing bodyHash<commit_after>var aws4 = exports,
    url = require('url'),
    querystring = require('querystring'),
    crypto = require('crypto'),
    lru = require('./lru'),
    credentialsCache = lru(1000)

// http://docs.amazonwebservices.com/general/latest/gr/signature-version-4.html

function hmac(key, string, encoding) {
  return crypto.createHmac('sha256', key).update(string, 'utf8').digest(encoding)
}

function hash(string, encoding) {
  return crypto.createHash('sha256').update(string, 'utf8').digest(encoding)
}

// This function assumes the string has already been percent encoded
function encodeRfc3986(urlEncodedString) {
  return urlEncodedString.replace(/[!'()*]/g, function(c) {
    return '%' + c.charCodeAt(0).toString(16).toUpperCase()
  })
}

// request: { path | body, [host], [method], [headers], [service], [region] }
// credentials: { accessKeyId, secretAccessKey, [sessionToken] }
function RequestSigner(request, credentials) {

  if (typeof request === 'string') request = url.parse(request)

  var headers = request.headers = (request.headers || {}),
      hostParts = this.matchHost(request.hostname || request.host || headers.Host || headers.host)

  this.request = request
  this.credentials = credentials || this.defaultCredentials()

  this.service = request.service || hostParts[0] || ''
  this.region = request.region || hostParts[1] || 'us-east-1'

  // SES uses a different domain from the service name
  if (this.service === 'email') this.service = 'ses'

  if (!request.method && request.body)
    request.method = 'POST'

  if (!headers.Host && !headers.host) {
    headers.Host = request.hostname || request.host || this.createHost()

    // If a port is specified explicitly, use it as is
    if (request.port)
      headers.Host += ':' + request.port
  }
  if (!request.hostname && !request.host)
    request.hostname = headers.Host || headers.host

  this.isCodeCommitGit = this.service === 'codecommit' && request.method === 'GIT'
}

RequestSigner.prototype.matchHost = function(host) {
  var match = (host || '').match(/([^\.]+)\.(?:([^\.]*)\.)?amazonaws\.com$/)
  var hostParts = (match || []).slice(1, 3)

  // ES's hostParts are sometimes the other way round, if the value that is expected
  // to be region equals ‘es’ switch them back
  // e.g. search-cluster-name-aaaa00aaaa0aaa0aaaaaaa0aaa.us-east-1.es.amazonaws.com
  if (hostParts[1] === 'es')
    hostParts = hostParts.reverse()

  return hostParts
}

// http://docs.aws.amazon.com/general/latest/gr/rande.html
RequestSigner.prototype.isSingleRegion = function() {
  // Special case for S3 and SimpleDB in us-east-1
  if (['s3', 'sdb'].indexOf(this.service) >= 0 && this.region === 'us-east-1') return true

  return ['cloudfront', 'ls', 'route53', 'iam', 'importexport', 'sts']
    .indexOf(this.service) >= 0
}

RequestSigner.prototype.createHost = function() {
  var region = this.isSingleRegion() ? '' :
        (this.service === 's3' && this.region !== 'us-east-1' ? '-' : '.') + this.region,
      service = this.service === 'ses' ? 'email' : this.service
  return service + region + '.amazonaws.com'
}

RequestSigner.prototype.prepareRequest = function() {
  this.parsePath()

  var request = this.request, headers = request.headers, query

  if (request.signQuery) {

    this.parsedPath.query = query = this.parsedPath.query || {}

    if (this.credentials.sessionToken)
      query['X-Amz-Security-Token'] = this.credentials.sessionToken

    if (this.service === 's3' && !query['X-Amz-Expires'])
      query['X-Amz-Expires'] = 86400

    if (query['X-Amz-Date'])
      this.datetime = query['X-Amz-Date']
    else
      query['X-Amz-Date'] = this.getDateTime()

    query['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256'
    query['X-Amz-Credential'] = this.credentials.accessKeyId + '/' + this.credentialString()
    query['X-Amz-SignedHeaders'] = this.signedHeaders()

  } else {

    if (!request.doNotModifyHeaders && !this.isCodeCommitGit) {
      if (request.body && !headers['Content-Type'] && !headers['content-type'])
        headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'

      if (request.body && !headers['Content-Length'] && !headers['content-length'])
        headers['Content-Length'] = Buffer.byteLength(request.body)

      if (this.credentials.sessionToken)
        headers['X-Amz-Security-Token'] = this.credentials.sessionToken

      if (this.service === 's3')
        headers['X-Amz-Content-Sha256'] = this.request.bodyHash || hash(this.request.body || '', 'hex')

      if (headers['X-Amz-Date'])
        this.datetime = headers['X-Amz-Date']
      else
        headers['X-Amz-Date'] = this.getDateTime()
    }

    delete headers.Authorization
    delete headers.authorization
  }
}

RequestSigner.prototype.sign = function() {
  if (!this.parsedPath) this.prepareRequest()

  if (this.request.signQuery) {
    this.parsedPath.query['X-Amz-Signature'] = this.signature()
  } else {
    this.request.headers.Authorization = this.authHeader()
  }

  this.request.path = this.formatPath()

  return this.request
}

RequestSigner.prototype.getDateTime = function() {
  if (!this.datetime) {
    var headers = this.request.headers,
      date = new Date(headers.Date || headers.date || new Date)

    this.datetime = date.toISOString().replace(/[:\-]|\.\d{3}/g, '')

    // Remove the trailing 'Z' on the timestamp string for CodeCommit git access
    if (this.isCodeCommitGit) this.datetime = this.datetime.slice(0, -1)
  }
  return this.datetime
}

RequestSigner.prototype.getDate = function() {
  return this.getDateTime().substr(0, 8)
}

RequestSigner.prototype.authHeader = function() {
  return [
    'AWS4-HMAC-SHA256 Credential=' + this.credentials.accessKeyId + '/' + this.credentialString(),
    'SignedHeaders=' + this.signedHeaders(),
    'Signature=' + this.signature(),
  ].join(', ')
}

RequestSigner.prototype.signature = function() {
  var date = this.getDate(),
      cacheKey = [this.credentials.secretAccessKey, date, this.region, this.service].join(),
      kDate, kRegion, kService, kCredentials = credentialsCache.get(cacheKey)
  if (!kCredentials) {
    kDate = hmac('AWS4' + this.credentials.secretAccessKey, date)
    kRegion = hmac(kDate, this.region)
    kService = hmac(kRegion, this.service)
    kCredentials = hmac(kService, 'aws4_request')
    credentialsCache.set(cacheKey, kCredentials)
  }
  return hmac(kCredentials, this.stringToSign(), 'hex')
}

RequestSigner.prototype.stringToSign = function() {
  return [
    'AWS4-HMAC-SHA256',
    this.getDateTime(),
    this.credentialString(),
    hash(this.canonicalString(), 'hex'),
  ].join('\n')
}

RequestSigner.prototype.canonicalString = function() {
  if (!this.parsedPath) this.prepareRequest()

  var pathStr = this.parsedPath.path,
      query = this.parsedPath.query,
      queryStr = '',
      normalizePath = this.service !== 's3',
      decodePath = this.service === 's3' || this.request.doNotEncodePath,
      decodeSlashesInPath = this.service === 's3',
      firstValOnly = this.service === 's3',
      bodyHash = this.service === 's3' && this.request.signQuery ? 'UNSIGNED-PAYLOAD' :
        (this.isCodeCommitGit ? '' : (this.request.bodyHash || hash(this.request.body || '', 'hex')))

  if (query) {
    queryStr = encodeRfc3986(querystring.stringify(Object.keys(query).sort().reduce(function(obj, key) {
      if (!key) return obj
      obj[key] = !Array.isArray(query[key]) ? query[key] :
        (firstValOnly ? query[key][0] : query[key].slice().sort())
      return obj
    }, {})))
  }
  if (pathStr !== '/') {
    if (normalizePath) pathStr = pathStr.replace(/\/{2,}/g, '/')
    pathStr = pathStr.split('/').reduce(function(path, piece) {
      if (normalizePath && piece === '..') {
        path.pop()
      } else if (!normalizePath || piece !== '.') {
        if (decodePath) piece = querystring.unescape(piece)
        path.push(encodeRfc3986(querystring.escape(piece)))
      }
      return path
    }, []).join('/')
    if (pathStr[0] !== '/') pathStr = '/' + pathStr
    if (decodeSlashesInPath) pathStr = pathStr.replace(/%2F/g, '/')
  }

  return [
    this.request.method || 'GET',
    pathStr,
    queryStr,
    this.canonicalHeaders() + '\n',
    this.signedHeaders(),
    bodyHash,
  ].join('\n')
}

RequestSigner.prototype.canonicalHeaders = function() {
  var headers = this.request.headers
  function trimAll(header) {
    return header.toString().trim().replace(/\s+/g, ' ')
  }
  return Object.keys(headers)
    .sort(function(a, b) { return a.toLowerCase() < b.toLowerCase() ? -1 : 1 })
    .map(function(key) { return key.toLowerCase() + ':' + trimAll(headers[key]) })
    .join('\n')
}

RequestSigner.prototype.signedHeaders = function() {
  return Object.keys(this.request.headers)
    .map(function(key) { return key.toLowerCase() })
    .sort()
    .join(';')
}

RequestSigner.prototype.credentialString = function() {
  return [
    this.getDate(),
    this.region,
    this.service,
    'aws4_request',
  ].join('/')
}

RequestSigner.prototype.defaultCredentials = function() {
  var env = process.env
  return {
    accessKeyId: env.AWS_ACCESS_KEY_ID || env.AWS_ACCESS_KEY,
    secretAccessKey: env.AWS_SECRET_ACCESS_KEY || env.AWS_SECRET_KEY,
    sessionToken: env.AWS_SESSION_TOKEN,
  }
}

RequestSigner.prototype.parsePath = function() {
  var path = this.request.path || '/',
      queryIx = path.indexOf('?'),
      query = null

  if (queryIx >= 0) {
    query = querystring.parse(path.slice(queryIx + 1))
    path = path.slice(0, queryIx)
  }

  // S3 doesn't always encode characters > 127 correctly and
  // all services don't encode characters > 255 correctly
  // So if there are non-reserved chars (and it's not already all % encoded), just encode them all
  if (/[^0-9A-Za-z!'()*\-._~%/]/.test(path)) {
    path = path.split('/').map(function(piece) {
      return querystring.escape(querystring.unescape(piece))
    }).join('/')
  }

  this.parsedPath = {
    path: path,
    query: query,
  }
}

RequestSigner.prototype.formatPath = function() {
  var path = this.parsedPath.path,
      query = this.parsedPath.query

  if (!query) return path

  // Services don't support empty query string keys
  if (query[''] != null) delete query['']

  return path + '?' + encodeRfc3986(querystring.stringify(query))
}

aws4.RequestSigner = RequestSigner

aws4.sign = function(request, credentials) {
  return new RequestSigner(request, credentials).sign()
}
<|endoftext|>


<commit_before>'use strict';

// Dependencies
var fs = require('fs'),
    path = require('path'),
    tinycolor = require("tinycolor2"),
    yaml = require('js-yaml');

var inputSchemes = process.argv[2],
    tempScheme = {
        scheme: [],
        author: [],
        base00: [],
        base01: [],
        base02: [],
        base03: [],
        base04: [],
        base05: [],
        base06: [],
        base07: [],
        base08: [],
        base09: [],
        base0A: [],
        base0B: [],
        base0C: [],
        base0D: [],
        base0E: [],
        base0F: []
    },
    mixedScheme = {};

if (typeof(inputSchemes) === 'undefined') throw new Error('Usage: b16m <Scheme 1>,<Scheme 2>');

inputSchemes = inputSchemes.split(',');
var numSchemes = inputSchemes.length;

if (numSchemes != 2) throw new Error('Missing ' + (2 - numSchemes) + ' inputSchemes to mix');

inputSchemes.forEach(function(scheme)
{
    var contents = yaml.safeLoad(fs.readFileSync(path.resolve(scheme), 'utf8'), function(err)
    {
        if (err) return console.log(err);
    });

    for (var prop in contents)
    {
        if (!contents.hasOwnProperty(prop)) continue;

        if (tempScheme[prop] === undefined) throw new Error('"' + prop + '" not a valid Base16 scheme property');
        tempScheme[prop].push(contents[prop]);
    }
});

for (var prop in tempScheme)
{
    if (!tempScheme.hasOwnProperty(prop)) continue;

    if (prop === 'scheme' || prop === 'author')
    {
        mixedScheme[prop] = uniques(tempScheme[prop]).join(' - ');
    }
    else
    {
        mixedScheme[prop] = tinycolor.mix.apply(null, tempScheme[prop]).toHex();
        console.log(tempScheme[prop][0] + ' + ' + tempScheme[prop][1] + ' --> ' + mixedScheme[prop]);
    }
}

var destPath = path.join(process.cwd(), mixedScheme.scheme + '.yml');

fs.writeFile(destPath, yaml.dump(mixedScheme), function(err)
{
    if (err) return console.log(err);
    console.log('Scheme saved to "' + destPath + '"');
});

// Utility Functions
function uniques(arr)
{
    var a = [];
    for (var i = 0, l = arr.length; i < l; i++)
    {
        if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
        {
            a.push(arr[i]);
        }
    }
    return a;
}
<commit_msg>Adhere to Airbnb JavaScript style guide<commit_after>(function() {
  'use strict';

  // Dependencies
  var fs = require('fs');
  var path = require('path');
  var tinycolor = require('tinycolor2');
  var yaml = require('js-yaml');

  // Utility Functions
  function uniques(arr) {
    var a = [];
    for (var i = 0, l = arr.length; i < l; i++) {
      if (a.indexOf(arr[i]) === -1 && arr[i] !== '') {
        a.push(arr[i]);
      }
    }

    return a;
  }

  var inputSchemes = process.argv[2];
  var tempScheme = {
    scheme: [],
    author: [],
    base00: [],
    base01: [],
    base02: [],
    base03: [],
    base04: [],
    base05: [],
    base06: [],
    base07: [],
    base08: [],
    base09: [],
    base0A: [],
    base0B: [],
    base0C: [],
    base0D: [],
    base0E: [],
    base0F: [],
  };
  var mixedScheme = {};

  if (typeof (inputSchemes) === 'undefined') {
    throw new Error('Usage: b16m <Scheme 1>,<Scheme 2>');
  }

  inputSchemes = inputSchemes.split(',');
  var numSchemes = inputSchemes.length;

  if (numSchemes !== 2) {
    throw new Error('Missing ' + (2 - numSchemes) + ' inputSchemes to mix');
  }

  inputSchemes.forEach(function(scheme) {
    var raw = fs.readFileSync(path.resolve(scheme), 'utf8');

    var contents = yaml.safeLoad(raw, function(err) {
      if (err) {
        return console.log(err);
      }
    });

    for (var prop in contents) {
      if (!contents.hasOwnProperty(prop)) {
        continue;
      }

      if (tempScheme[prop] === undefined) {
        throw new Error('"' + prop + '" not a valid Base16 scheme property');
      }

      tempScheme[prop].push(contents[prop]);
    }
  });

  for (var prop in tempScheme) {
    if (!tempScheme.hasOwnProperty(prop)) {
      continue;
    }

    if (prop === 'scheme' || prop === 'author') {
      mixedScheme[prop] = uniques(tempScheme[prop]).join(' - ');
    } else {
      mixedScheme[prop] = tinycolor.mix.apply(null, tempScheme[prop]).toHex();
      console.log(tempScheme[prop][0] + ' + ' + tempScheme[prop][1] + ' --> ' + mixedScheme[prop]);
    }
  }

  var destPath = path.join(process.cwd(), mixedScheme.scheme + '.yml');

  fs.writeFile(destPath, yaml.dump(mixedScheme), function(err) {
    if (err) {
      return console.log(err);
    }

    console.log('Scheme saved to "' + destPath + '"');
  });
}());
<|endoftext|>


<commit_before>module.exports = function () {
  return new EntityManager()
}

var Entity = require('./Entity.js')
var createPool = require('reuse-pool')
var getName = require('typedef').getName

/**
 * Manage, create, and destroy entities. Can use methods to mutate entities
 * (tags, components) directly or via the facade on the Entity.
 * @constructor
 */
function EntityManager () {
  /**
   * Map of tags to the list of their entities.
   * @private
   */
  this._tags = {}

  /**
   * @type {Array.<Entity>}
   * @private
   */
  this._entities = []

  /**
   * @type {Array.<Group>}
   * @private
   */
  this._groups = {}

  /**
   * Pool entities.
   * @private
   */
  this._entityPool = createPool(function () { return new Entity() })

  /**
   * Map of component names to their respective object pools.
   * @private
   */
  this._componentPools = {}
}

/**
 * Used for indexing our component groups.
 * @constructor
 * @param {Array.<Function>} Components
 * @param {Array<Entity>} entities
 */
function Group (Components, entities) {
  this.Components = Components || []
  this.entities = entities || []
}

/**
 * Get a new entity.
 * @return {Entity}
 */
EntityManager.prototype.createEntity = function () {
  var entity = this._entityPool.get()

  this._entities.push(entity)
  entity._manager = this
  return entity
}

/**
 * Cleanly remove entities based on tag. Avoids loop issues.
 * @param {String} tag
 */
EntityManager.prototype.removeEntitiesByTag = function (tag) {
  var entities = this._tags[tag]

  if (!entities) return

  for (var x = entities.length - 1; x >= 0; x--) {
    var entity = entities[x]
    entity.remove()
  }
}

/**
 * Dump all entities out of the manager. Avoids loop issues.
 */
EntityManager.prototype.removeAllEntities = function () {
  for (var x = this._entities.length - 1; x >= 0; x--) {
    this._entities[x].remove()
  }
}

/**
 * Drop an entity. Returns it to the pool and fires all events for removing
 * components as well.
 * @param {Entity} entity
 */
EntityManager.prototype.removeEntity = function (entity) {
  var index = this._entities.indexOf(entity)

  if (!~index) {
    throw new Error('Tried to remove entity not in list')
  }

  this.entityRemoveAllComponents(entity)

  // Remove from entity list
  // entity.emit('removed')
  this._entities.splice(index, 1)

  // Remove entity from any tag groups and clear the on-entity ref
  entity._tags.length = 0
  for (var tag in this._tags) {
    var entities = this._tags[tag]
    var n = entities.indexOf(entity)
    if (~n) entities.splice(n, 1)
  }

  // Prevent any acecss and free
  entity._manager = null
  this._entityPool.recycle(entity)
  entity.removeAllListeners()
}

/**
 * @param {Entity} entity
 * @param {String} tag
 */
EntityManager.prototype.entityAddTag = function (entity, tag) {
  var entities = this._tags[tag]

  if (!entities) {
    entities = this._tags[tag] = []
  }

  // Don't add if already there
  if (~entities.indexOf(entity)) return

  // Add to our tag index AND the list on the entity
  entities.push(entity)
  entity._tags.push(tag)
}

/**
 * @param {Entity} entity
 * @param {String} tag
 */
EntityManager.prototype.entityRemoveTag = function (entity, tag) {
  var entities = this._tags[tag]
  if (!entities) return

  var index = entities.indexOf(entity)
  if (!~index) return

  // Remove from our index AND the list on the entity
  entities.splice(index, 1)
  entity._tags.splice(entity._tags.indexOf(tag), 1)
}

/**
 * @param {Entity} entity
 * @param {Function} Component
 */
EntityManager.prototype.entityAddComponent = function (entity, Component) {
  if (~entity._Components.indexOf(Component)) return

  entity._Components.push(Component)

  // Create the reference on the entity to this component
  var cName = componentPropertyName(Component)
  entity[cName] = new Component(entity)

  // Check each indexed group to see if we need to add this entity to the list
  for (var groupName in this._groups) {
    var group = this._groups[groupName]

    // Only add this entity to a group index if this component is in the group,
    // this entity has all the components of the group, and its not already in
    // the index.
    if (!~group.Components.indexOf(Component)) {
      continue
    }
    if (!entity.hasAllComponents(group.Components)) {
      continue
    }
    if (~group.entities.indexOf(entity)) {
      continue
    }

    group.entities.push(entity)
  }

  entity.emit('component added', Component)
}

/**
 * Drop all components on an entity. Avoids loop issues.
 * @param {Entity} entity
 */
EntityManager.prototype.entityRemoveAllComponents = function (entity) {
  var Cs = entity._Components

  for (var j = Cs.length - 1; j >= 0; j--) {
    var C = Cs[j]
    entity.removeComponent(C)
  }
}

/**
 * @param {Entity} entity
 * @param {Function} Component
 */
EntityManager.prototype.entityRemoveComponent = function (entity, Component) {
  var index = entity._Components.indexOf(Component)
  if (!~index) return

  entity.emit('component removed', Component)

  // Check each indexed group to see if we need to remove it
  for (var groupName in this._groups) {
    var group = this._groups[groupName]

    if (!~group.Components.indexOf(Component)) {
      continue
    }
    if (!entity.hasAllComponents(group.Components)) {
      continue
    }

    var loc = group.entities.indexOf(entity)
    if (~loc) {
      group.entities.splice(loc, 1)
    }
  }

  // Remove T listing on entity and property ref, then free the component.
  var propName = componentPropertyName(Component)
  entity._Components.splice(index, 1)
  delete entity[propName]
}

/**
 * Get a list of entities that have a certain set of components.
 * @param {Array.<Function>} Components
 * @return {Array.<Entity>}
 */
EntityManager.prototype.queryComponents = function (Components) {
  var group = this._groups[groupKey(Components)]

  if (!group) {
    group = this._indexGroup(Components)
  }

  return group.entities
}

/**
 * Get a list of entities that all have a certain tag.
 * @param {String} tag
 * @return {Array.<Entity>}
 */
EntityManager.prototype.queryTag = function (tag) {
  var entities = this._tags[tag]

  if (entities === undefined) {
    entities = this._tags[tag] = []
  }

  return entities
}

/**
 * @return {Number} Total number of entities.
 */
EntityManager.prototype.count = function () {
  return this._entities.length
}

/**
 * Create an index of entities with a set of components.
 * @param {Array.<Function>} Components
 * @private
 */
EntityManager.prototype._indexGroup = function (Components) {
  var key = groupKey(Components)

  if (this._groups[key]) return

  var group = this._groups[key] = new Group(Components)

  for (var n = 0; n < this._entities.length; n++) {
    var entity = this._entities[n]
    if (entity.hasAllComponents(Components)) {
      group.entities.push(entity)
    }
  }

  return group
}

/**
 * @param {Function} Component
 * @return {String}
 * @private
 */
function componentPropertyName (Component) {
  var name = getName(Component)
  return name.charAt(0).toLowerCase() + name.slice(1)
}

/**
 * @param {Array.<Function>} Components
 * @return {String}
 * @private
 */
function groupKey (Components) {
  var names = []
  for (var n = 0; n < Components.length; n++) {
    var T = Components[n]
    names.push(getName(T))
  }

  return names
    .map(function (x) { return x.toLowerCase() })
    .sort()
    .join('-')
}
<commit_msg>add backref from component to owning entity<commit_after>module.exports = function () {
  return new EntityManager()
}

var Entity = require('./Entity.js')
var createPool = require('reuse-pool')
var getName = require('typedef').getName

/**
 * Manage, create, and destroy entities. Can use methods to mutate entities
 * (tags, components) directly or via the facade on the Entity.
 * @constructor
 */
function EntityManager () {
  /**
   * Map of tags to the list of their entities.
   * @private
   */
  this._tags = {}

  /**
   * @type {Array.<Entity>}
   * @private
   */
  this._entities = []

  /**
   * @type {Array.<Group>}
   * @private
   */
  this._groups = {}

  /**
   * Pool entities.
   * @private
   */
  this._entityPool = createPool(function () { return new Entity() })

  /**
   * Map of component names to their respective object pools.
   * @private
   */
  this._componentPools = {}
}

/**
 * Used for indexing our component groups.
 * @constructor
 * @param {Array.<Function>} Components
 * @param {Array<Entity>} entities
 */
function Group (Components, entities) {
  this.Components = Components || []
  this.entities = entities || []
}

/**
 * Get a new entity.
 * @return {Entity}
 */
EntityManager.prototype.createEntity = function () {
  var entity = this._entityPool.get()

  this._entities.push(entity)
  entity._manager = this
  return entity
}

/**
 * Cleanly remove entities based on tag. Avoids loop issues.
 * @param {String} tag
 */
EntityManager.prototype.removeEntitiesByTag = function (tag) {
  var entities = this._tags[tag]

  if (!entities) return

  for (var x = entities.length - 1; x >= 0; x--) {
    var entity = entities[x]
    entity.remove()
  }
}

/**
 * Dump all entities out of the manager. Avoids loop issues.
 */
EntityManager.prototype.removeAllEntities = function () {
  for (var x = this._entities.length - 1; x >= 0; x--) {
    this._entities[x].remove()
  }
}

/**
 * Drop an entity. Returns it to the pool and fires all events for removing
 * components as well.
 * @param {Entity} entity
 */
EntityManager.prototype.removeEntity = function (entity) {
  var index = this._entities.indexOf(entity)

  if (!~index) {
    throw new Error('Tried to remove entity not in list')
  }

  this.entityRemoveAllComponents(entity)

  // Remove from entity list
  // entity.emit('removed')
  this._entities.splice(index, 1)

  // Remove entity from any tag groups and clear the on-entity ref
  entity._tags.length = 0
  for (var tag in this._tags) {
    var entities = this._tags[tag]
    var n = entities.indexOf(entity)
    if (~n) entities.splice(n, 1)
  }

  // Prevent any acecss and free
  entity._manager = null
  this._entityPool.recycle(entity)
  entity.removeAllListeners()
}

/**
 * @param {Entity} entity
 * @param {String} tag
 */
EntityManager.prototype.entityAddTag = function (entity, tag) {
  var entities = this._tags[tag]

  if (!entities) {
    entities = this._tags[tag] = []
  }

  // Don't add if already there
  if (~entities.indexOf(entity)) return

  // Add to our tag index AND the list on the entity
  entities.push(entity)
  entity._tags.push(tag)
}

/**
 * @param {Entity} entity
 * @param {String} tag
 */
EntityManager.prototype.entityRemoveTag = function (entity, tag) {
  var entities = this._tags[tag]
  if (!entities) return

  var index = entities.indexOf(entity)
  if (!~index) return

  // Remove from our index AND the list on the entity
  entities.splice(index, 1)
  entity._tags.splice(entity._tags.indexOf(tag), 1)
}

/**
 * @param {Entity} entity
 * @param {Function} Component
 */
EntityManager.prototype.entityAddComponent = function (entity, Component) {
  if (~entity._Components.indexOf(Component)) return

  entity._Components.push(Component)

  // Create the reference on the entity to this component
  var cName = componentPropertyName(Component)
  entity[cName] = new Component(entity)

  entity[cName].entity = entity

  // Check each indexed group to see if we need to add this entity to the list
  for (var groupName in this._groups) {
    var group = this._groups[groupName]

    // Only add this entity to a group index if this component is in the group,
    // this entity has all the components of the group, and its not already in
    // the index.
    if (!~group.Components.indexOf(Component)) {
      continue
    }
    if (!entity.hasAllComponents(group.Components)) {
      continue
    }
    if (~group.entities.indexOf(entity)) {
      continue
    }

    group.entities.push(entity)
  }

  entity.emit('component added', Component)
}

/**
 * Drop all components on an entity. Avoids loop issues.
 * @param {Entity} entity
 */
EntityManager.prototype.entityRemoveAllComponents = function (entity) {
  var Cs = entity._Components

  for (var j = Cs.length - 1; j >= 0; j--) {
    var C = Cs[j]
    entity.removeComponent(C)
  }
}

/**
 * @param {Entity} entity
 * @param {Function} Component
 */
EntityManager.prototype.entityRemoveComponent = function (entity, Component) {
  var index = entity._Components.indexOf(Component)
  if (!~index) return

  entity.emit('component removed', Component)

  // Check each indexed group to see if we need to remove it
  for (var groupName in this._groups) {
    var group = this._groups[groupName]

    if (!~group.Components.indexOf(Component)) {
      continue
    }
    if (!entity.hasAllComponents(group.Components)) {
      continue
    }

    var loc = group.entities.indexOf(entity)
    if (~loc) {
      group.entities.splice(loc, 1)
    }
  }

  // Remove T listing on entity and property ref, then free the component.
  var propName = componentPropertyName(Component)
  entity._Components.splice(index, 1)
  delete entity[propName]
}

/**
 * Get a list of entities that have a certain set of components.
 * @param {Array.<Function>} Components
 * @return {Array.<Entity>}
 */
EntityManager.prototype.queryComponents = function (Components) {
  var group = this._groups[groupKey(Components)]

  if (!group) {
    group = this._indexGroup(Components)
  }

  return group.entities
}

/**
 * Get a list of entities that all have a certain tag.
 * @param {String} tag
 * @return {Array.<Entity>}
 */
EntityManager.prototype.queryTag = function (tag) {
  var entities = this._tags[tag]

  if (entities === undefined) {
    entities = this._tags[tag] = []
  }

  return entities
}

/**
 * @return {Number} Total number of entities.
 */
EntityManager.prototype.count = function () {
  return this._entities.length
}

/**
 * Create an index of entities with a set of components.
 * @param {Array.<Function>} Components
 * @private
 */
EntityManager.prototype._indexGroup = function (Components) {
  var key = groupKey(Components)

  if (this._groups[key]) return

  var group = this._groups[key] = new Group(Components)

  for (var n = 0; n < this._entities.length; n++) {
    var entity = this._entities[n]
    if (entity.hasAllComponents(Components)) {
      group.entities.push(entity)
    }
  }

  return group
}

/**
 * @param {Function} Component
 * @return {String}
 * @private
 */
function componentPropertyName (Component) {
  var name = getName(Component)
  return name.charAt(0).toLowerCase() + name.slice(1)
}

/**
 * @param {Array.<Function>} Components
 * @return {String}
 * @private
 */
function groupKey (Components) {
  var names = []
  for (var n = 0; n < Components.length; n++) {
    var T = Components[n]
    names.push(getName(T))
  }

  return names
    .map(function (x) { return x.toLowerCase() })
    .sort()
    .join('-')
}
<|endoftext|>


<commit_before>const EventEmitter = require('events');
const kafka = require('kafka-node');
// const DEFAULT_SEND_OPTIONS = {topicSuffix : ''};
 /**
  * @typedef {Object} SendOption
  *
  * @property {String} [topicSuffix=''] The suffix of the topic name.
  */

 /**
  * @typedef {Object} ProducerOption
  *
  * @class KafkaProducer
  * @param {String} name The name of current instance.
  * @param {String=} kafkaHost The host of the broker of kafka, when both of `zookeeperHost` and `kafkaHost` passed, the `kafkaHost` has higher priority.
  * @param {String=} topic The topic where you save data in it.
  * @param {Number=} delayInterval When pass this parameter, messages will publish to kafka every `option.delayInterval` ms, otherwise messages will publish to kafka at once.
  */

/**
 * The class of the producer of Kafka
 * @class KafkaProducer
 * @extends {EventEmitter}
 */
class KafkaProducer extends EventEmitter {
    /**
     * Creates an instance of KafkaProducer.
     *
     * @param {ProducerOption} option
     * @memberof KafkaProducer
     */
    constructor({
        name,
        topic,
        kafkaHost,
        delayInterval,
    }) {
        super();
        this.name = name;
        this.producer = null;
        this._clientPromise = null;
        this._clientOk = false;
        if (!kafkaHost) {
            throw new Error('You must giva a  kafkaHost.');
        }
        this.topic = topic;
        if (!this.topic) {
            throw new Error('You must give the topic paramemter.');
        }
        this._createProducer(kafkaHost);

        this.delayInterval = delayInterval;
        this._delayData = {};

        this._delayTimer = null;
        if (delayInterval > 0) {
            this._doSendDataTimer();
        }
    }
    /**
     * @private
     *
     * @memberof KafkaProducer
     */
    _cancleTimer() {
        if (this._delayTimer) {
            clearTimeout(this._delayTimer);
        }
    }
    /**
     * @private
     * @param {String} zookeeperHost
     *
     * @memberof KafkaProducer
     */

    _createProducer(kafkaHost) {
        const client = new kafka.KafkaClient({kafkaHost: kafkaHost});
        const _this = this;

        _this._clientPromise = new Promise(function(resolve,reject) {
            client.on('ready',function() {
                _this.emit(KafkaProducer.EVENT_CLIENT_READY);

            });
            client.on('error',function(err) {
                _this._cancleTimer();
                _this._clientOk = false;
                reject(err);
                return _this.emit(KafkaProducer.EVENT_CLIENT_ERROR,err);
            });
            const producer = new kafka.HighLevelProducer(client);
            producer.on('ready', function producerReady() {

                _this.producer = producer;
                _this._clientOk = true;
                resolve();
                return _this.emit(KafkaProducer.EVENT_PRODUCER_READY);

            });
            producer.on('error',function producerError(err) {
                _this._cancleTimer();
                _this._clientOk = false;
                reject(err);
                return _this.emit(KafkaProducer.EVENT_PRODUCER_ERROR,err);
            });
        });
    }

    /**
     * @private
     *
     * @memberof KafkaProducer
     */
    _doSendDataTimer() {
        const _this = this;
        const _delayData = this._delayData;
        this._delayTimer = setTimeout(function delayProcess() {
            if (!_this.producer) {
                return _this._doSendDataTimer();
            }
            const keys = Object.keys(_delayData);
            const arrayLen = keys.length;
            if (arrayLen === 0) {
                return _this._doSendDataTimer();
            }
            for (var i=0;i<arrayLen;i++) {
                const topicSuffix = keys[i];
                const topicData = _delayData[topicSuffix];
                const dataArray = topicData.splice(0);
                if (dataArray.length > 0) {
                    _this.producer.send(
                        _this._createSendData(dataArray,{topicSuffix}),function(err) {
                            if (err) {
                                _this.emit(KafkaProducer.EVENT_SEND_ERROR,err,dataArray);
                            }
                            _this.emit(
                                KafkaProducer.EVENT_DELAY_MESSAGE_SEND_FINISHED,
                                err,dataArray.length
                            );
                        }
                    );
                }

            }
            _this._doSendDataTimer();
        },this.delayInterval);

    }
    /**
     * @private
     *
     * @param {Object} taskData
     * @param {SendOption} options
     * @returns
     * @memberof KafkaProducer
     */
    _createSendData(taskData, options) {
        const suffix = options.topicSuffix || '';
        return [{
            topic: this.topic + suffix,
            messages: taskData,
        }];
    }
    _getSendData(taskData) {
        return typeof(taskData) === 'object' ?
        JSON.stringify(taskData):
        taskData;
    }
    /**
     *
     * @private
     * @param {Object} taskData
     * @param {SendOption} options
     * @param {Function} callback
     * @memberof KafkaProducer
     */
    _sendAtOnce(taskData,options,callback) {
        const _this = this;
        this.producer.send(
            this._createSendData(this._getSendData(taskData), options),
            function(err,data) {
                callback(err,data);
                if (err) {
                    _this.emit(KafkaProducer.EVENT_SEND_ERROR,err,taskData);
                }
            }
        );
    }

    /**
     * Send data to kafka, it will send the data to kafka every `delayInterval` ms when `delayInterval` is set. It will wait the client i
     *
     * @param {Object} taskData
     * @param {SendOption} options
     * @param {Function=} [callback=function() {}]
     * @returns {KafkaProducer}
     * @memberof KafkaProducer
     */
    addData(taskData, options = { topicSuffix : '' }, callback = function() {}) {
        // options = options || {topicSuffix : ''};
        const topicSuffix = options.topicSuffix || '';
        if (this.delayInterval > 0) {
            if (!this._delayData[topicSuffix]) {
                this._delayData[topicSuffix] = [];
            }
            this._delayData[topicSuffix].push(this._getSendData(taskData));
            return this;
        }
        if (this._clientOk) {
            this._sendAtOnce(taskData,options,callback);
        } else {
            const _this = this;
            this._clientPromise.then(function() {
                _this._sendAtOnce(taskData,options,callback)
            }).catch(function(err) {
                callback(err);
            });
        }

        return this;
    }

}
/**
 * The event to notify that the client is ready.
 */
KafkaProducer.EVENT_CLIENT_READY = 'eventClientReady';
/**
 * The event to notify that the client is error.
 */
KafkaProducer.EVENT_CLIENT_ERROR = 'eventClientError';

/**
 * The event to notify that a batch of messages have been sent finished.
 */
KafkaProducer.EVENT_DELAY_MESSAGE_SEND_FINISHED = 'eventDelayMessageSendFinished';
/**
 * The event to notify that the producer is ready.
 */
KafkaProducer.EVENT_PRODUCER_READY = 'eventProducerReady';
/**
 * The event to notify the producer is error.
 */
KafkaProducer.EVENT_PRODUCER_ERROR = 'eventProducerError';

/**
 * The event emitted when an error occurs after sending data to kafka.
 */
KafkaProducer.EVENT_SEND_ERROR = 'eventSendError';

module.exports = KafkaProducer;
<commit_msg>emit event when kafkaClient closed.<commit_after>const EventEmitter = require('events');
const kafka = require('kafka-node');
// const DEFAULT_SEND_OPTIONS = {topicSuffix : ''};
 /**
  * @typedef {Object} SendOption
  *
  * @property {String} [topicSuffix=''] The suffix of the topic name.
  */

 /**
  * @typedef {Object} ProducerOption
  *
  * @class KafkaProducer
  * @param {String} name The name of current instance.
  * @param {String=} kafkaHost The host of the broker of kafka, when both of `zookeeperHost` and `kafkaHost` passed, the `kafkaHost` has higher priority.
  * @param {String=} topic The topic where you save data in it.
  * @param {Number=} delayInterval When pass this parameter, messages will publish to kafka every `option.delayInterval` ms, otherwise messages will publish to kafka at once.
  */

/**
 * The class of the producer of Kafka
 * @class KafkaProducer
 * @extends {EventEmitter}
 */
class KafkaProducer extends EventEmitter {
    /**
     * Creates an instance of KafkaProducer.
     *
     * @param {ProducerOption} option
     * @memberof KafkaProducer
     */
    constructor({
        name,
        topic,
        kafkaHost,
        delayInterval,
    }) {
        super();
        this.name = name;
        this.producer = null;
        this._clientPromise = null;
        this._clientOk = false;
        if (!kafkaHost) {
            throw new Error('You must giva a  kafkaHost.');
        }
        this.topic = topic;
        if (!this.topic) {
            throw new Error('You must give the topic paramemter.');
        }
        this._createProducer(kafkaHost);

        this.delayInterval = delayInterval;
        this._delayData = {};

        this._delayTimer = null;
        if (delayInterval > 0) {
            this._doSendDataTimer();
        }
    }
    /**
     * @private
     *
     * @memberof KafkaProducer
     */
    _cancleTimer() {
        if (this._delayTimer) {
            clearTimeout(this._delayTimer);
        }
    }
    /**
     * @private
     * @param {String} zookeeperHost
     *
     * @memberof KafkaProducer
     */

    _createProducer(kafkaHost) {
        const client = new kafka.KafkaClient({kafkaHost: kafkaHost});
        const _this = this;

        _this._clientPromise = new Promise(function(resolve,reject) {
            client.on('ready',function() {
                _this.emit(KafkaProducer.EVENT_CLIENT_READY);

            });
            client.on('error',function(err) {
                _this._cancleTimer();
                _this._clientOk = false;
                reject(err);
                return _this.emit(KafkaProducer.EVENT_CLIENT_ERROR,err);
            });
            client.on('close',function() {
                _this.emit(KafkaProducer.EVENT_CLIENT_CLOSE);
            });
            const producer = new kafka.HighLevelProducer(client);
            producer.on('ready', function producerReady() {

                _this.producer = producer;
                _this._clientOk = true;
                resolve();
                return _this.emit(KafkaProducer.EVENT_PRODUCER_READY);

            });
            producer.on('error',function producerError(err) {
                _this._cancleTimer();
                _this._clientOk = false;
                reject(err);
                return _this.emit(KafkaProducer.EVENT_PRODUCER_ERROR,err);
            });
        });
    }

    /**
     * @private
     *
     * @memberof KafkaProducer
     */
    _doSendDataTimer() {
        const _this = this;
        const _delayData = this._delayData;
        this._delayTimer = setTimeout(function delayProcess() {
            if (!_this.producer) {
                return _this._doSendDataTimer();
            }
            const keys = Object.keys(_delayData);
            const arrayLen = keys.length;
            if (arrayLen === 0) {
                return _this._doSendDataTimer();
            }
            for (var i=0;i<arrayLen;i++) {
                const topicSuffix = keys[i];
                const topicData = _delayData[topicSuffix];
                const dataArray = topicData.splice(0);
                if (dataArray.length > 0) {
                    _this.producer.send(
                        _this._createSendData(dataArray,{topicSuffix}),function(err) {
                            if (err) {
                                _this.emit(KafkaProducer.EVENT_SEND_ERROR,err,dataArray);
                            }
                            _this.emit(
                                KafkaProducer.EVENT_DELAY_MESSAGE_SEND_FINISHED,
                                err,dataArray.length
                            );
                        }
                    );
                }

            }
            _this._doSendDataTimer();
        },this.delayInterval);

    }
    /**
     * @private
     *
     * @param {Object} taskData
     * @param {SendOption} options
     * @returns
     * @memberof KafkaProducer
     */
    _createSendData(taskData, options) {
        const suffix = options.topicSuffix || '';
        return [{
            topic: this.topic + suffix,
            messages: taskData,
        }];
    }
    _getSendData(taskData) {
        return typeof(taskData) === 'object' ?
        JSON.stringify(taskData):
        taskData;
    }
    /**
     *
     * @private
     * @param {Object} taskData
     * @param {SendOption} options
     * @param {Function} callback
     * @memberof KafkaProducer
     */
    _sendAtOnce(taskData,options,callback) {
        const _this = this;
        this.producer.send(
            this._createSendData(this._getSendData(taskData), options),
            function(err,data) {
                callback(err,data);
                if (err) {
                    _this.emit(KafkaProducer.EVENT_SEND_ERROR,err,taskData);
                }
            }
        );
    }

    /**
     * Send data to kafka, it will send the data to kafka every `delayInterval` ms when `delayInterval` is set. It will wait the client i
     *
     * @param {Object} taskData
     * @param {SendOption} options
     * @param {Function=} [callback=function() {}]
     * @returns {KafkaProducer}
     * @memberof KafkaProducer
     */
    addData(taskData, options = { topicSuffix : '' }, callback = function() {}) {
        // options = options || {topicSuffix : ''};
        const topicSuffix = options.topicSuffix || '';
        if (this.delayInterval > 0) {
            if (!this._delayData[topicSuffix]) {
                this._delayData[topicSuffix] = [];
            }
            this._delayData[topicSuffix].push(this._getSendData(taskData));
            return this;
        }
        if (this._clientOk) {
            this._sendAtOnce(taskData,options,callback);
        } else {
            const _this = this;
            this._clientPromise.then(function() {
                _this._sendAtOnce(taskData,options,callback)
            }).catch(function(err) {
                callback(err);
            });
        }

        return this;
    }

}
/**
 * The event to notify that the client is ready.
 */
KafkaProducer.EVENT_CLIENT_READY = 'eventClientReady';
/**
 * The event to notify that the client is error.
 */
KafkaProducer.EVENT_CLIENT_ERROR = 'eventClientError';

/**
 * The event of notify that the client is closed.
 */
KafkaProducer.EVENT_CLIENT_CLOSE = 'eventClientClose';

/**
 * The event to notify that a batch of messages have been sent finished.
 */
KafkaProducer.EVENT_DELAY_MESSAGE_SEND_FINISHED = 'eventDelayMessageSendFinished';
/**
 * The event to notify that the producer is ready.
 */
KafkaProducer.EVENT_PRODUCER_READY = 'eventProducerReady';
/**
 * The event to notify the producer is error.
 */
KafkaProducer.EVENT_PRODUCER_ERROR = 'eventProducerError';

/**
 * The event emitted when an error occurs after sending data to kafka.
 */
KafkaProducer.EVENT_SEND_ERROR = 'eventSendError';

module.exports = KafkaProducer;
<|endoftext|>