<commit_before>/* global fetch */
import LokkaTransport from 'lokka/transport';
// In some envionment like in ReactNative, we don't need fetch at all.
// Technically, this should be handle by 'isomorphic-fetch'.
// But it's not happening. So this is the fix

let fetchUrl;
if (typeof fetch === 'function') {
  // has a native fetch implementation
  fetchUrl = fetch;
} else if (typeof __dirname !== 'undefined') {
  // for Node.js
  fetchUrl = require('node-fetch');
  fetchUrl.Promise = Promise;
} else {
  // for the browser
  fetchUrl = require('whatwg-fetch');
}

export class Transport extends LokkaTransport {
  constructor(endpoint, options = {}) {
    if (!endpoint) {
      throw new Error('endpoint is required!');
    }

    super();
    this._httpOptions = {
      auth: options.auth,
      headers: options.headers || {}
    };
    this.endpoint = endpoint;
  }

  _buildOptions(payload) {
    const options = {
      method: 'post',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(payload),
      // To pass cookies to the server. (supports CORS as well)
      credentials: 'include',
    };

    Object.assign(options.headers, this._httpOptions.headers);
    return options;
  }

  send(query, variables, operationName) {
    const payload = {query, variables, operationName};
    const options = this._buildOptions(payload);

    return fetchUrl(this.endpoint, options).then(response => {
      // 200 is for success
      // 400 is for bad request
      if (response.status !== 200 && response.status !== 400) {
        throw new Error(`Invalid status code: ${response.status}`);
      }

      return response.json();
    }).then(({data, errors}) => {
      if (errors) {
        const message = errors[0].message;
        const error = new Error(`GraphQL Error: ${message}`);
        error.rawError = errors;

        throw error;
      }

      return data;
    });
  }
}

export default Transport;
<commit_msg>optional CORS credentials support<commit_after>/* global fetch */
import LokkaTransport from 'lokka/transport';
// In some envionment like in ReactNative, we don't need fetch at all.
// Technically, this should be handle by 'isomorphic-fetch'.
// But it's not happening. So this is the fix

let fetchUrl;
if (typeof fetch === 'function') {
  // has a native fetch implementation
  fetchUrl = fetch;
} else if (typeof __dirname !== 'undefined') {
  // for Node.js
  fetchUrl = require('node-fetch');
  fetchUrl.Promise = Promise;
} else {
  // for the browser
  fetchUrl = require('whatwg-fetch');
}

export class Transport extends LokkaTransport {
  constructor(endpoint, options = {}) {
    if (!endpoint) {
      throw new Error('endpoint is required!');
    }

    super();
    this._httpOptions = {
      auth: options.auth,
      headers: options.headers || {},
      credentials: options.credentials,
    };
    this.endpoint = endpoint;
  }

  _buildOptions(payload) {
    const options = {
      method: 'post',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(payload),
      // To pass cookies to the server. (supports CORS as well)
      credentials: 'include',
    };

    // use delete property for backward compatibility
    if(this._httpOptions.credentials === false) delete options.credentials;

    Object.assign(options.headers, this._httpOptions.headers);
    return options;
  }

  send(query, variables, operationName) {
    const payload = {query, variables, operationName};
    const options = this._buildOptions(payload);

    return fetchUrl(this.endpoint, options).then(response => {
      // 200 is for success
      // 400 is for bad request
      if (response.status !== 200 && response.status !== 400) {
        throw new Error(`Invalid status code: ${response.status}`);
      }

      return response.json();
    }).then(({data, errors}) => {
      if (errors) {
        const message = errors[0].message;
        const error = new Error(`GraphQL Error: ${message}`);
        error.rawError = errors;

        throw error;
      }

      return data;
    });
  }
}

export default Transport;
<|endoftext|>


<commit_before>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*
 * Copyright (c) 2015, Joyent, Inc.
 */

var assert = require('assert-plus');
var cp = require('child_process');
var execFile = cp.execFile;
var spawn = cp.spawn;

var VMADM_PATH = '/usr/sbin/vmadm';

function vmadm() {}

function execVmadm(opts, callback) {
    assert.object(opts, 'opts');
    assert.object(opts.log, 'opts.log');
    assert.arrayOfString(opts.args, 'opts.args');
    var args = opts.args;

    opts.log.info({ args: args }, 'executing vmadm');

    return execFile(VMADM_PATH, args, function (err, stdout, stderr) {
        return callback(err, stdout, stderr);
    });
}

/**
 * Call `vmadm lookup -j UUID`.
 *
 * @param opts {Object} Options
 *      - uuid {String} The VM uuid.
 *      - log {Logger object}
 * @param vmopts {Object} Optional vm options
 *      - fields {Array} Return only the keys give in `fields` array
 * @param callback {Function} `function (err)`
 */

vmadm.load = function vmLoad(opts, vmopts, callback) {
    assert.object(opts, 'opts');
    assert.string(opts.uuid, 'opts.uuid');

    if (!callback) {
        callback = vmopts;
    }

    function addargs(newargs) {
        Array.prototype.push.apply(args, newargs);
    }

    var args = ['lookup', '-1', '-j'];

    if (vmopts && vmopts.fields) {
        addargs(['-o', opts.fields.join(',')]);
    }

    args.push('uuid=' + opts.uuid);

    var execopts = {
        args: args,
        log: opts.log
    };

    execVmadm(execopts, function (err, stdout, stderr) {
        if (err) {
            return callback(err);
        }
        var vms;

        try {
            vms = JSON.parse(stdout);
        } catch (jsonErr) {
            jsonErr.stdout = stdout;
            return callback(jsonErr);
        }

        var vm;
        if (vms.length) {
            vm = vms[0];
        }

        return callback(null, vm);
    });
};

/**
 * Call `vmadm create`.
 *
 * @param opts {Object} Options
 *      - log {Logger object}
 * @param callback {Function} `function (err)`
 */

vmadm.create = function vmCreate(opts, callback) {
    assert.object(opts, 'opts');
    assert.object(opts.log, 'opts.log');

    var log = opts.log;
    delete opts.log;
    log.info('spawning vmadm for machine create');

    var vmadmproc = spawn(VMADM_PATH, ['create']);
    var stderr = '';

    vmadmproc.stdin.write(JSON.stringify(opts));
    vmadmproc.stdin.end();

    vmadmproc.on('close', function (code) {
        if (code) {
            callback(new Error(stderr.trim()));
            return;
        }
        callback();
    });

    vmadmproc.stderr.on('data', function (data) {
        stderr += data.toString();
    });
};

/**
 * Call `vmadm delete <uuid>`.
 *
 * @param opts {Object} Options
 *      - uuid {String} UUID of VM to delete
 *      - log {Logger object}
 * @param callback {Function} `function (err)`
 */

vmadm.delete = function vmDelete(opts, callback) {
    assert.object(opts, 'opts');
    assert.string(opts.uuid, 'opts.uuid');
    assert.object(opts.log, 'opts.log');

    var args = ['delete', opts.uuid];

    var execopts = {
        args: args,
        log: opts.log
    };

    execVmadm(execopts, function (err, stdout, stderr) {
        if (err) {
            return callback(err);
        }
        return callback();
    });
};

/**
 * Call `vmadm update`.
 *
 * @param opts {Object} VMADM update payload
 *      - log {Logger object}
 * @param callback {Function} `function (err)`
 */

vmadm.update = function vmUpdate(opts, callback) {
    assert.object(opts, 'opts');
    assert.string(opts.uuid, 'opts.uuid');
    assert.object(opts.log, 'opts.log');

    var log = opts.log;
    delete opts.log;
    log.info('spawning vmadm for machine update');

    var vmadmproc = spawn(VMADM_PATH, ['update', opts.uuid]);
    var stderr = '';

    vmadmproc.stdin.write(JSON.stringify(opts));
    vmadmproc.stdin.end();

    vmadmproc.on('close', function (code) {
        if (code) {
            callback(new Error(stderr.trim()));
            return;
        }
        callback();
    });

    vmadmproc.stderr.on('data', function (data) {
        stderr += data.toString();
    });
};

module.exports = vmadm;
<commit_msg>Add vmadm.reboot<commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*
 * Copyright (c) 2015, Joyent, Inc.
 */

var assert = require('assert-plus');
var cp = require('child_process');
var execFile = cp.execFile;
var spawn = cp.spawn;

var VMADM_PATH = '/usr/sbin/vmadm';

function vmadm() {}

function execVmadm(opts, callback) {
    assert.object(opts, 'opts');
    assert.object(opts.log, 'opts.log');
    assert.arrayOfString(opts.args, 'opts.args');
    var args = opts.args;

    opts.log.info({ args: args }, 'executing vmadm');

    return execFile(VMADM_PATH, args, function (err, stdout, stderr) {
        return callback(err, stdout, stderr);
    });
}

/**
 * Call `vmadm lookup -j UUID`.
 *
 * @param opts {Object} Options
 *      - uuid {String} The VM uuid.
 *      - log {Logger object}
 * @param vmopts {Object} Optional vm options
 *      - fields {Array} Return only the keys give in `fields` array
 * @param callback {Function} `function (err)`
 */

vmadm.load = function vmLoad(opts, vmopts, callback) {
    assert.object(opts, 'opts');
    assert.string(opts.uuid, 'opts.uuid');

    if (!callback) {
        callback = vmopts;
    }

    function addargs(newargs) {
        Array.prototype.push.apply(args, newargs);
    }

    var args = ['lookup', '-1', '-j'];

    if (vmopts && vmopts.fields) {
        addargs(['-o', opts.fields.join(',')]);
    }

    args.push('uuid=' + opts.uuid);

    var execopts = {
        args: args,
        log: opts.log
    };

    execVmadm(execopts, function (err, stdout, stderr) {
        if (err) {
            return callback(err);
        }
        var vms;

        try {
            vms = JSON.parse(stdout);
        } catch (jsonErr) {
            jsonErr.stdout = stdout;
            return callback(jsonErr);
        }

        var vm;
        if (vms.length) {
            vm = vms[0];
        }

        return callback(null, vm);
    });
};

/**
 * Call `vmadm create`.
 *
 * @param opts {Object} Options
 *      - log {Logger object}
 * @param callback {Function} `function (err)`
 */

vmadm.create = function vmCreate(opts, callback) {
    assert.object(opts, 'opts');
    assert.object(opts.log, 'opts.log');

    var log = opts.log;
    delete opts.log;
    log.info('spawning vmadm for machine create');

    var vmadmproc = spawn(VMADM_PATH, ['create']);
    var stderr = '';

    vmadmproc.stdin.write(JSON.stringify(opts));
    vmadmproc.stdin.end();

    vmadmproc.on('close', function (code) {
        if (code) {
            callback(new Error(stderr.trim()));
            return;
        }
        callback();
    });

    vmadmproc.stderr.on('data', function (data) {
        stderr += data.toString();
    });
};

/**
 * Call `vmadm delete <uuid>`.
 *
 * @param opts {Object} Options
 *      - uuid {String} UUID of VM to delete
 *      - log {Logger object}
 * @param callback {Function} `function (err)`
 */

vmadm.delete = function vmDelete(opts, callback) {
    assert.object(opts, 'opts');
    assert.string(opts.uuid, 'opts.uuid');
    assert.object(opts.log, 'opts.log');

    var args = ['delete', opts.uuid];

    var execopts = {
        args: args,
        log: opts.log
    };

    execVmadm(execopts, function (err, stdout, stderr) {
        if (err) {
            return callback(err);
        }
        return callback();
    });
};

/**
 * Call `vmadm update`.
 *
 * @param opts {Object} VMADM update payload
 *      - log {Logger object}
 * @param callback {Function} `function (err)`
 */

vmadm.update = function vmUpdate(opts, callback) {
    assert.object(opts, 'opts');
    assert.string(opts.uuid, 'opts.uuid');
    assert.object(opts.log, 'opts.log');

    var log = opts.log;
    delete opts.log;
    log.info('spawning vmadm for machine update');

    var vmadmproc = spawn(VMADM_PATH, ['update', opts.uuid]);
    var stderr = '';

    vmadmproc.stdin.write(JSON.stringify(opts));
    vmadmproc.stdin.end();

    vmadmproc.on('close', function (code) {
        if (code) {
            callback(new Error(stderr.trim()));
            return;
        }
        callback();
    });

    vmadmproc.stderr.on('data', function (data) {
        stderr += data.toString();
    });
};

/**
 * Call `vmadm reboot <uuid>`.
 *
 * @param opts {Object} Options
 *      - uuid {String} UUID of VM to reboot
 *      - force {Boolean} Whether to force the reboot.
 *      - log {Logger object}
 * @param callback {Function} `function (err)`
 */

vmadm.reboot = function vmReboot(opts, callback) {
    assert.object(opts, 'opts');
    assert.string(opts.uuid, 'opts.uuid');
    assert.object(opts.log, 'opts.log');

    var args = ['reboot', opts.uuid];

    if (opts.force) {
        args.push('-F');
    }

    var execopts = {
        args: args,
        log: opts.log
    };

    execVmadm(execopts, function (err, stdout, stderr) {
        if (err) {
            return callback(err);
        }
        return callback();
    });
};

module.exports = vmadm;
<|endoftext|>


<commit_before>var builder = require('./builder/builder');
var endpoint = require('./endpoint/');
var middleWare = require('./middleware/');

module.exports = function(options){
	var b = builder(options.defaultOptionMethod);
	b.createEndpoints = function(){
		return endpoint(b.getRoot());
	};
	b.createMW = function(badRequest, notAllowed){
		var endpoints = this.createEndpoints();
		return middleWare(endpoints, badRequest, notAllowed);
	};
	return b;
};<commit_msg>Using default empty option for router<commit_after>var builder = require('./builder/builder');
var endpoint = require('./endpoint/');
var middleWare = require('./middleware/');

module.exports = function(options){
	options = options || {};
	var b = builder(options.defaultOptionMethod);
	b.createEndpoints = function(){
		return endpoint(b.getRoot());
	};
	b.createMW = function(badRequest, notAllowed){
		var endpoints = this.createEndpoints();
		return middleWare(endpoints, badRequest, notAllowed);
	};
	return b;
};<|endoftext|>


<commit_before>'use strict';

/**
 * Dependencies
 */
var koa = require('koa');
var compose = require('koa-compose');
var views = require('co-views');
var route = require('koa-route');
var stack = require('./middleware').stack;
var dispatch = require('./dispatcher').dispatch;
var path = require('path');

/**
 * Application
 */
module.exports = function (config) {
  config = config || {};
  config.middleware = config.middleware || {};
  config.options = config.options || {};

  /**
   * Original Koa.js application
   */
  var app = koa();

  /**
   * Session support
   */
  if (typeof config.options.cookies.keys !== 'undefined')
    app.keys = config.options.cookies.keys;

  /**
   * Middleware stack
   */
  var middleware = stack(config);

  /**
   * Routes
   * @todo provide routing for different types of HTTP-requests
   */
  var routes = config.routes;
  for (var urlPath in routes) {
    middleware.push(route.get(urlPath, dispatch(routes[urlPath])));
  }

  /**
   * Compose
   */
  app.use(compose(middleware));

  /**
   * View renderer
   */
  app.render = views(path.join(process.cwd(), 'views'), { ext: 'ejs' });

  return app;
};
<commit_msg>Application options added to the app.<commit_after>'use strict';

/**
 * Dependencies
 */
var koa = require('koa');
var compose = require('koa-compose');
var views = require('co-views');
var route = require('koa-route');
var stack = require('./middleware').stack;
var dispatch = require('./dispatcher').dispatch;
var path = require('path');

/**
 * Application
 */
module.exports = function (config) {
  config = config || {};
  config.middleware = config.middleware || {};
  config.options = config.options || {};

  /**
   * Original Koa.js application
   */
  var app = koa();

  /**
   * Application options
   */
  app.options = config.options;

  /**
   * Session support
   */
  if (typeof config.options.cookies.keys !== 'undefined')
    app.keys = config.options.cookies.keys;

  /**
   * Middleware stack
   */
  var middleware = stack(config);

  /**
   * Routes
   * @todo provide routing for different types of HTTP-requests
   */
  var routes = config.routes;
  for (var urlPath in routes) {
    middleware.push(route.get(urlPath, dispatch(routes[urlPath])));
  }

  /**
   * Compose
   */
  app.use(compose(middleware));

  /**
   * View renderer
   */
  app.render = views(path.join(process.cwd(), 'views'), { ext: 'ejs' });

  return app;
};
<|endoftext|>


<commit_before>var cp = require('child_process');
var path = require('path');
var Q = require('q');
var EventEmitter = require('events').EventEmitter;
var util = require('./util');
var HEARTBEAT_INTERVAL = util.HEARTBEAT_INTERVAL;
var HEARTBEAT_TIMEOUT = util.HEARTBEAT_TIMEOUT;
var MESSAGE = util.MESSAGE;
var DATA = util.DATA;
var ERROR = util.ERROR;
var MAIN = path.join(__dirname, 'main');
var cache = {};

function getKey(options) {
  var key = options && options.script;
  if (typeof key != 'string' || !(key = key.trim())) {
    return;
  }

  if (typeof options.value == 'string') {
    key = encodeURIComponent(key) + '?value=' + encodeURIComponent(options.value);
  }
  return key;
}

function fork(options, callback) {
  var key = getKey(options);
  if (!key) {
    return callback('options.script is required.');
  }
  var promise = cache[key];
  var defer;

  if (!promise) {
    defer = Q.defer();
    cache[key] = promise = defer.promise;
  }

  promise.done(function(result) {
    callback(null, result[0], result[1]);
  }, function(err) {
    callback(err);
  });

  if (!defer) {
    return;
  }

  var child, timeout, heartbeatTimeout, done, errMsg;
  var emitter = new EventEmitter();
  var args = Array.isArray(options._args) ? options._args : [];
  delete options._args;
  args.push(MAIN, encodeURIComponent(JSON.stringify(options)));

  try {
    child = cp.spawn(options.execPath || 'node', args, {
      detached: true,
      stdio: ['ipc']
    });
    child.on('error', errorHandler);
    child.on('close', errorHandler);
    child.on('exit', errorHandler);
    child.on('disconnect', errorHandler);
    child.on('message', function(msg) {
      clearTimeout(heartbeatTimeout);
      heartbeatTimeout = null;
      if (msg == MESSAGE) {
        return;
      }

      try {
        if (msg = JSON.parse(msg)) {
          if (msg.type == MESSAGE) {
            callbackHandler(null, msg.data);
          } else if (msg.type == DATA) {
            emitter.emit('data', msg.data);
          } else if (msg.type == ERROR) {
            callback(msg.data);
          }
        }
      } catch(e) {}
    });
    if (child.stderr) {
      child.stderr.on('data', function(data) {
        if (done || !Buffer.isBuffer(data)) {
          return;
        }
        errMsg = errMsg ? Buffer.concat([errMsg, data]) : data;
      });
    }
    child.unref();
    keepAlive();
    promise.kill = killChild;
  } catch(err) {
    errorHandler(err);
  }

  function keepAlive() {
    timeout = setTimeout(function() {
      try {
        child.send(MESSAGE);
        keepAlive();
      } catch (err) {}
    }, HEARTBEAT_INTERVAL);
    if (!heartbeatTimeout) {
      heartbeatTimeout = setTimeout(errorHandler, HEARTBEAT_TIMEOUT);
    }
  }

  function errorHandler(err) {
    killChild();
    process.nextTick(function() {
      errMsg = errMsg && errMsg + '';
      callbackHandler(err && err.stack || errMsg || 'unknown');
    });
  }

  function killChild(delay) {
    clearTimeout(heartbeatTimeout);
    clearTimeout(timeout);
    delete cache[key];
    var _kill = function() {
      try {
        process.kill(child.pid);
      } catch(e) {}
      try {
        child.removeAllListeners();
        child.on('error', util.noop);
        if (child.stderr) {
          child.stderr.removeAllListeners();
        }
      } catch(e) {}
      emitter.kill = util.noop;
      emitter.sendData = util.noop;
      emitter.emit('exit');
    };
    delay > 0 ? setTimeout(_kill, delay) : _kill();
  }

  function callbackHandler(err, data) {
    if (done) {
      return;
    }
    done = true;
    if (err) {
      defer.reject(err);
    } else {
      emitter.kill = killChild;
      emitter.sendData = function(data) {
        try {
          child.send(JSON.stringify({
            type: DATA,
            data: data
          }));
        } catch(e) {}
      };
      defer.resolve([data, emitter]);
    }
  }
}

exports.fork = fork;

function kill(options, delay) {
  var key = getKey(options);
  if (!key) {
    return;
  }
  var child = cache[key];
  if (!child) {
    return;
  }
  child.kill(delay);
}

exports.kill = kill;

function exists(options) {
  var key = getKey(options);
  if (!key || !cache[key]) {
    return false;
  }

  return true;
}

exports.exists = exists;

<commit_msg>refactor: set maxHeaderSize=256000<commit_after>var cp = require('child_process');
var path = require('path');
var Q = require('q');
var EventEmitter = require('events').EventEmitter;
var util = require('./util');
var HEARTBEAT_INTERVAL = util.HEARTBEAT_INTERVAL;
var HEARTBEAT_TIMEOUT = util.HEARTBEAT_TIMEOUT;
var MESSAGE = util.MESSAGE;
var DATA = util.DATA;
var ERROR = util.ERROR;
var MAIN = path.join(__dirname, 'main');
var cache = {};

function getKey(options) {
  var key = options && options.script;
  if (typeof key != 'string' || !(key = key.trim())) {
    return;
  }

  if (typeof options.value == 'string') {
    key = encodeURIComponent(key) + '?value=' + encodeURIComponent(options.value);
  }
  return key;
}

function fork(options, callback) {
  var key = getKey(options);
  if (!key) {
    return callback('options.script is required.');
  }
  var promise = cache[key];
  var defer;

  if (!promise) {
    defer = Q.defer();
    cache[key] = promise = defer.promise;
  }

  promise.done(function(result) {
    callback(null, result[0], result[1]);
  }, function(err) {
    callback(err);
  });

  if (!defer) {
    return;
  }

  var child, timeout, heartbeatTimeout, done, errMsg;
  var emitter = new EventEmitter();
  var args = Array.isArray(options._args) ? options._args : [];
  delete options._args;
  args.push('--max-http-header-size=256000', MAIN, encodeURIComponent(JSON.stringify(options)));

  try {
    child = cp.spawn(options.execPath || 'node', args, {
      detached: true,
      stdio: ['ipc']
    });
    child.on('error', errorHandler);
    child.on('close', errorHandler);
    child.on('exit', errorHandler);
    child.on('disconnect', errorHandler);
    child.on('message', function(msg) {
      clearTimeout(heartbeatTimeout);
      heartbeatTimeout = null;
      if (msg == MESSAGE) {
        return;
      }

      try {
        if (msg = JSON.parse(msg)) {
          if (msg.type == MESSAGE) {
            callbackHandler(null, msg.data);
          } else if (msg.type == DATA) {
            emitter.emit('data', msg.data);
          } else if (msg.type == ERROR) {
            callback(msg.data);
          }
        }
      } catch(e) {}
    });
    if (child.stderr) {
      child.stderr.on('data', function(data) {
        if (done || !Buffer.isBuffer(data)) {
          return;
        }
        errMsg = errMsg ? Buffer.concat([errMsg, data]) : data;
      });
    }
    child.unref();
    keepAlive();
    promise.kill = killChild;
  } catch(err) {
    errorHandler(err);
  }

  function keepAlive() {
    timeout = setTimeout(function() {
      try {
        child.send(MESSAGE);
        keepAlive();
      } catch (err) {}
    }, HEARTBEAT_INTERVAL);
    if (!heartbeatTimeout) {
      heartbeatTimeout = setTimeout(errorHandler, HEARTBEAT_TIMEOUT);
    }
  }

  function errorHandler(err) {
    killChild();
    process.nextTick(function() {
      errMsg = errMsg && errMsg + '';
      callbackHandler(err && err.stack || errMsg || 'unknown');
    });
  }

  function killChild(delay) {
    clearTimeout(heartbeatTimeout);
    clearTimeout(timeout);
    delete cache[key];
    var _kill = function() {
      try {
        process.kill(child.pid);
      } catch(e) {}
      try {
        child.removeAllListeners();
        child.on('error', util.noop);
        if (child.stderr) {
          child.stderr.removeAllListeners();
        }
      } catch(e) {}
      emitter.kill = util.noop;
      emitter.sendData = util.noop;
      emitter.emit('exit');
    };
    delay > 0 ? setTimeout(_kill, delay) : _kill();
  }

  function callbackHandler(err, data) {
    if (done) {
      return;
    }
    done = true;
    if (err) {
      defer.reject(err);
    } else {
      emitter.kill = killChild;
      emitter.sendData = function(data) {
        try {
          child.send(JSON.stringify({
            type: DATA,
            data: data
          }));
        } catch(e) {}
      };
      defer.resolve([data, emitter]);
    }
  }
}

exports.fork = fork;

function kill(options, delay) {
  var key = getKey(options);
  if (!key) {
    return;
  }
  var child = cache[key];
  if (!child) {
    return;
  }
  child.kill(delay);
}

exports.kill = kill;

function exists(options) {
  var key = getKey(options);
  if (!key || !cache[key]) {
    return false;
  }

  return true;
}

exports.exists = exists;

<|endoftext|>


<commit_before>"use strict";

/**
 * 查找字串所在位置（从 0 开始索引）。
 * @param {string} p_text 待查文本。
 * @param {string} p_pattern 子串。
 * @param {boolean} [p_recursive] 查找所有字串的位置。
 * @returns {Number|Array}
 */
module.exports.boyer_moore = function (p_text, p_pattern, p_recursive)
{
    // 非法字符串，返回 -1。
    if (!p_text || typeof p_text !== "string" || p_text === "" || !p_pattern || typeof p_pattern !== "string" || p_pattern === "")
    {
        return -1;
    }
    else
    {
        const recursive = p_recursive || false;
        let result = recursive ? [] : -1;

        const lengthT = p_text.length;
        const lengthP = p_pattern.length;
        const suffixPosition = lengthP - 1;

        const badTable = _badTable(p_pattern);
        const goodIndex = _getGoodIndex(p_pattern);

        let i = 0;
        let shift;
        let badShift = -1;
        let goodShift = -1;
        let goodSuffix = "";
        let currentCharT;
        let currentCharP;
        while (i <= lengthT - lengthP)
        {
            for (let j = lengthP - 1; j >= 0; j--)
            {
                currentCharP = p_pattern[j];
                currentCharT = p_text[i + j];

                if (currentCharP === currentCharT)
                {
                    // 如果全部相等，就输出匹配。
                    if (j === 0)
                    {
                        // 如果需递归查找，则保存结果（并不需要重置 goodSuffix）。
                        if (recursive)
                        {
                            result.push(i);
                        }
                        else
                        {
                            return i;
                        }
                    }
                    else
                    {
                        // 好后缀。
                        goodSuffix = currentCharP + goodSuffix;
                    }
                }
                else
                {
                    // 坏字符。
                    badShift = j - _getBadIndex(currentCharT, badTable);
                    if (goodSuffix !== "")
                    {
                        goodShift = suffixPosition - goodIndex;
                        goodSuffix = "";
                        shift = Math.max(badShift, goodShift);
                    }
                    else
                    {
                        shift = badShift;
                    }
                    break;
                }
            }

            i = i + shift;
        }

        if (recursive && result.length === 0)
        {
            result = -1;
        }

        return result;
    }
};

/**
 * 查找“坏字符”第一次出现的位置。
 */
function _getBadIndex(p_char, p_badTable)
{
    const index = p_badTable[p_char];
    return (index === undefined) ? -1 : index;
}

/**
 * 计算“坏字符规则表”。
 */
function _badTable(p_pattern)
{
    const result = {};
    const length = p_pattern.length;
    for (let i = 0; i < length; i++)
    {
        result[p_pattern[i]] = i;
    }
    return result;
}

/**
 * 计算“好后缀规则表”，实际上就是一个整数值。
 */
function _getGoodIndex(p_pattern)
{
    let goodIndex = -1;

    let affix;
    let suffix;
    let subAffix;
    let lengthAffix;
    let lengthSuffix;
    const lengthP = p_pattern.length;
    for (let i = lengthP - 1; i > 0; i--)
    {
        affix = p_pattern.slice(0, i);
        suffix = p_pattern.slice(i);
        lengthAffix = affix.length;
        lengthSuffix = suffix.length;

        subAffix = affix.slice(0, lengthSuffix);
        if (subAffix === suffix)
        {
            // 从最短的后缀找起，如果找到与 pattern 的首位匹配，直接结束。
            goodIndex = lengthSuffix - 1;
            break;
        }
        else
        {
            for (let j = 1; j < lengthAffix - lengthSuffix + 1; j++)
            {
                subAffix = affix.slice(j, j + lengthSuffix);
                if (subAffix === suffix)
                {
                    if (goodIndex === -1)
                    {
                        goodIndex = j + lengthSuffix - 1;
                    }
                    else
                    {
                        // 在没有找到首位出现的情况下，取最小值。
                        goodIndex = Math.min(goodIndex, j + lengthSuffix - 1);
                    }
                    break;
                }
            }
        }
    }

    return goodIndex;
}

/**
 * 计算“好后缀规则表”。
 * 方法：拿到当前所有“好后缀”，从最长的开始，搜索其在 Pattern 中的“上一次出现位置”；如果未找到，则选短一点的，以此类推，直到最短的好后缀；如果都未找到，
 */
function _getGoodSuffixShifts(p_pattern)
{
}<commit_msg>rename badTable to badCharacters<commit_after>"use strict";

/**
 * 查找字串所在位置（从 0 开始索引）。
 * @param {string} p_text 待查文本。
 * @param {string} p_pattern 子串。
 * @param {boolean} [p_recursive] 查找所有字串的位置。
 * @returns {Number|Array}
 */
module.exports.boyer_moore = function (p_text, p_pattern, p_recursive)
{
    // 非法字符串，返回 -1。
    if (!p_text || typeof p_text !== "string" || p_text === "" || !p_pattern || typeof p_pattern !== "string" || p_pattern === "")
    {
        return -1;
    }
    else
    {
        const recursive = p_recursive || false;
        let result = recursive ? [] : -1;

        const lengthT = p_text.length;
        const lengthP = p_pattern.length;
        const suffixPosition = lengthP - 1;

        const badCharacters = _badCharacters(p_pattern);
        const goodIndex = _getGoodIndex(p_pattern);

        let i = 0;
        let shift;
        let badShift = -1;
        let goodShift = -1;
        let goodSuffix = "";
        let currentCharT;
        let currentCharP;
        while (i <= lengthT - lengthP)
        {
            for (let j = lengthP - 1; j >= 0; j--)
            {
                currentCharP = p_pattern[j];
                currentCharT = p_text[i + j];

                if (currentCharP === currentCharT)
                {
                    // 如果全部相等，就输出匹配。
                    if (j === 0)
                    {
                        // 如果需递归查找，则保存结果（并不需要重置 goodSuffix）。
                        if (recursive)
                        {
                            result.push(i);
                        }
                        else
                        {
                            return i;
                        }
                    }
                    else
                    {
                        // 好后缀。
                        goodSuffix = currentCharP + goodSuffix;
                    }
                }
                else
                {
                    // 坏字符。
                    badShift = j - _getBadIndex(currentCharT, badCharacters);
                    if (goodSuffix !== "")
                    {
                        goodShift = suffixPosition - goodIndex;
                        goodSuffix = "";
                        shift = Math.max(badShift, goodShift);
                    }
                    else
                    {
                        shift = badShift;
                    }
                    break;
                }
            }

            i = i + shift;
        }

        if (recursive && result.length === 0)
        {
            result = -1;
        }

        return result;
    }
};

/**
 * 查找“坏字符”第一次出现的位置。
 */
function _getBadIndex(p_char, p_badTable)
{
    const index = p_badTable[p_char];
    return (index === undefined) ? -1 : index;
}

/**
 * 计算“坏字符规则表”。
 */
function _badCharacters(p_pattern)
{
    const result = {};
    const length = p_pattern.length;
    for (let i = 0; i < length; i++)
    {
        result[p_pattern[i]] = i;
    }
    return result;
}

/**
 * 计算“好后缀规则表”，实际上就是一个整数值。
 */
function _getGoodIndex(p_pattern)
{
    let goodIndex = -1;

    let affix;
    let suffix;
    let subAffix;
    let lengthAffix;
    let lengthSuffix;
    const lengthP = p_pattern.length;
    for (let i = lengthP - 1; i > 0; i--)
    {
        affix = p_pattern.slice(0, i);
        suffix = p_pattern.slice(i);
        lengthAffix = affix.length;
        lengthSuffix = suffix.length;

        subAffix = affix.slice(0, lengthSuffix);
        if (subAffix === suffix)
        {
            // 从最短的后缀找起，如果找到与 pattern 的首位匹配，直接结束。
            goodIndex = lengthSuffix - 1;
            break;
        }
        else
        {
            for (let j = 1; j < lengthAffix - lengthSuffix + 1; j++)
            {
                subAffix = affix.slice(j, j + lengthSuffix);
                if (subAffix === suffix)
                {
                    if (goodIndex === -1)
                    {
                        goodIndex = j + lengthSuffix - 1;
                    }
                    else
                    {
                        // 在没有找到首位出现的情况下，取最小值。
                        goodIndex = Math.min(goodIndex, j + lengthSuffix - 1);
                    }
                    break;
                }
            }
        }
    }

    return goodIndex;
}

/**
 * 计算“好后缀规则表”。
 * 方法：拿到当前所有“好后缀”，从最长的开始，搜索其在 Pattern 中的“上一次出现位置”；如果未找到，则选短一点的，以此类推，直到最短的好后缀；如果都未找到，
 */
function _getGoodSuffixShifts(p_pattern)
{
}<|endoftext|>


<commit_before>'use strict';

var restify = require('restify');
var session = require('client-sessions');
var async = require('async');
var rarity = require('rarity');
var mongoose = require('mongoose');
var url = require('url');
var kue = require('kue');
var http = require('http');
var https = require('https');
var Anyfetch = require('anyfetch');
var lru = require('lru-cache');

var Token = require('./models/token.js');
var TempToken = require('./models/temp-token.js');

var connected = false;
var connectMongo = function(mongoUrl) {
  if(!connected) {
    // Connect mongoose
    mongoose.connect(mongoUrl || process.env.MONGO_URL || "mongodb://localhost/anyfetch-provider");
    connected = true;
  }
};

/**
* Simple wrapper around the token model.
* Only one token should match.
*
* retrieveData({accessToken: ...}) => data for this access token
* retrieveData({'data.grant': ...}) => data with this value of grant.
*/
module.exports.retrieveData = function(hash, cb) {
  Token.findOne(hash, function(err, token) {
    if(!token) {
      return cb(new Error("no data matches"));
    }
    cb(err, token.data);
  });
};

/**
 * Create a new provider server.
 * This server will use `config.task_generator` as its main function, to turn a file into metadata.
 *
 * @param {Object} config Configuration hash.
 *   Mandatory:
*       task_generator, to retrieve a list of tasks (a task is "a document to upload").
*       task_handler, the uploading function.
*    Optional:
*       concurrency, max number of simultaneous calls to your task_handler function (default is 1)
 */
module.exports.createServer = function createServer(connectFunctions, updateAccount, workers, config) {
  // Connect mongo
  connectMongo(config.mongoUrl);

  // Load endpoints generators
  var indexEndpoint = require('./handlers/index.js');
  var initConnectEndpoints = require('./handlers/init/connect.js');
  var initCallbackEndpoints = require('./handlers/init/callback.js');
  var updateEndpoint = require('./handlers/update.js');
  var statusEndpoint = require('./handlers/status.js');
  var resetEndpoint = require('./handlers/reset.js');

  http.globalAgent.maxSockets = config.maxSockets || 30;
  https.globalAgent.maxSockets = http.globalAgent.maxSockets;

  // Create server
  var server = restify.createServer();
  server.use(restify.acceptParser(server.acceptable));
  server.use(restify.queryParser());
  server.use(restify.bodyParser());
  server.use(session({
    cookieName: 'ANYFETCH_SESSION',
    secret: config.appSecret
  }));

  var components = url.parse(config.redisUrl || process.env.REDIS_URL || "redis://localhost:6379");
  server.queue = kue.createQueue({
    prefix: config.providerUrl,
    redis: {
      port: components.port || 6379,
      host: components.hostname || "localhost",
      auth: components.auth,
    },
    disableSearch: true
  });

  // When jobs are completed, we can delete them
  server.queue.on('job complete', function(id) {
    kue.Job.get(id, function(err, job) {
      if(err) {
        return console.log("ERROR", err);
      }

      job.remove();
    });
  });

  function sigtermKue() {
    server.queue.shutdown(function(err) {
      console.log('Kue is shut down.', err || '');
      process.exit(0);
    }, 5000);
  }

  process.once('SIGTERM', sigtermKue);

  // One element have a length of '1', so we can stock 50 elements
  server.tokenCache = lru({
    max: 50,
    length: function () { return 1; }
  });

  server.userCache = lru({
    max: 50,
    length: function () { return 1; }
  });

  Object.keys(workers).forEach(function createQueue(name) {
    server.queue.process(name, (workers[name].concurrency) ? workers[name].concurrency : 1, function(job, done) {
      job.task = job.data;

      job.anyfetchClient = new Anyfetch(job.task._anyfetchToken);
      job.anyfetchClient.setApiUrl(job.task._anyfetchApiUrl);

      async.waterfall([
        function retrieveToken(cb) {
          if(server.tokenCache.has(job.task._anyfetchToken)) {
            cb(null, server.tokenCache.get(job.task._anyfetchToken));
          }
          else {
            Token.findOne({anyfetchToken: job.task._anyfetchToken}, rarity.slice(2, cb));
          }
        },
        function executeTask(token, cb) {
          if(!token) {
            return cb(new Error("Can't retrieve Token"));
          }

          console.log("User `" + token.accountName + "` is executing task " + job.id + " onto `" + name + "`");

          server.tokenCache.set(job.task._anyfetchToken, token);

          job.serviceData = token.data;
          job.cache = server.userCache;

          // We use task instead of data
          delete job.data;
          delete job.task._anyfetchToken;
          delete job.task._anyfetchApiUrl;

          workers[name](job, cb);
        }
      ], done);
    });
  });

  // Load routes and generate endpoints using closures
  server.get('/', indexEndpoint);
  server.get('/init/connect', initConnectEndpoints(connectFunctions.redirectToService, config));
  server.get('/init/callback', initCallbackEndpoints(connectFunctions.retrieveTokens, config));

  server.post('/update', updateEndpoint(updateAccount, server.queue, workers));

  server.get('/status', statusEndpoint);
  server.del('/reset', resetEndpoint);

  // Expose the server
  return server;
};

module.exports.debug = {
  cleanTokens: function(cb) {
    connectMongo();
    async.parallel([
      function(cb) {
        Token.remove({}, cb);
      },
      function(cb) {
        TempToken.remove({}, cb);
      }
    ], cb);
  },
  createToken: function(hash, cb) {
    connectMongo();

    var token = new Token(hash);
    token.save(cb);
  },
  createTempToken: function(hash, cb) {
    connectMongo();

    var tempToken = new TempToken(hash);
    tempToken.save(cb);
  }
};
<commit_msg>Fix a bug in REDIS_URL<commit_after>'use strict';

var restify = require('restify');
var session = require('client-sessions');
var async = require('async');
var rarity = require('rarity');
var mongoose = require('mongoose');
var url = require('url');
var kue = require('kue');
var http = require('http');
var https = require('https');
var Anyfetch = require('anyfetch');
var lru = require('lru-cache');

var Token = require('./models/token.js');
var TempToken = require('./models/temp-token.js');

var connected = false;
var connectMongo = function(mongoUrl) {
  if(!connected) {
    // Connect mongoose
    mongoose.connect(mongoUrl || process.env.MONGO_URL || "mongodb://localhost/anyfetch-provider");
    connected = true;
  }
};

/**
* Simple wrapper around the token model.
* Only one token should match.
*
* retrieveData({accessToken: ...}) => data for this access token
* retrieveData({'data.grant': ...}) => data with this value of grant.
*/
module.exports.retrieveData = function(hash, cb) {
  Token.findOne(hash, function(err, token) {
    if(!token) {
      return cb(new Error("no data matches"));
    }
    cb(err, token.data);
  });
};

/**
 * Create a new provider server.
 * This server will use `config.task_generator` as its main function, to turn a file into metadata.
 *
 * @param {Object} config Configuration hash.
 *   Mandatory:
*       task_generator, to retrieve a list of tasks (a task is "a document to upload").
*       task_handler, the uploading function.
*    Optional:
*       concurrency, max number of simultaneous calls to your task_handler function (default is 1)
 */
module.exports.createServer = function createServer(connectFunctions, updateAccount, workers, config) {
  // Connect mongo
  connectMongo(config.mongoUrl);

  // Load endpoints generators
  var indexEndpoint = require('./handlers/index.js');
  var initConnectEndpoints = require('./handlers/init/connect.js');
  var initCallbackEndpoints = require('./handlers/init/callback.js');
  var updateEndpoint = require('./handlers/update.js');
  var statusEndpoint = require('./handlers/status.js');
  var resetEndpoint = require('./handlers/reset.js');

  http.globalAgent.maxSockets = config.maxSockets || 30;
  https.globalAgent.maxSockets = http.globalAgent.maxSockets;

  // Create server
  var server = restify.createServer();
  server.use(restify.acceptParser(server.acceptable));
  server.use(restify.queryParser());
  server.use(restify.bodyParser());
  server.use(session({
    cookieName: 'ANYFETCH_SESSION',
    secret: config.appSecret
  }));

  var components = url.parse(config.redisUrl || process.env.REDIS_URL || "redis://localhost");
  server.queue = kue.createQueue({
    prefix: config.providerUrl,
    redis: {
      port: components.port || 6379,
      host: components.hostname || "localhost",
      auth: (components.auth) ? ((components.auth.split(':').length > 1) ? components.auth.split(':')[1] : components.auth) : undefined,
    },
    disableSearch: true
  });

  // When jobs are completed, we can delete them
  server.queue.on('job complete', function(id) {
    kue.Job.get(id, function(err, job) {
      if(err) {
        return console.log("ERROR", err);
      }

      job.remove();
    });
  });

  function sigtermKue() {
    server.queue.shutdown(function(err) {
      console.log('Kue is shut down.', err || '');
      process.exit(0);
    }, 5000);
  }

  process.once('SIGTERM', sigtermKue);

  // One element have a length of '1', so we can stock 50 elements
  server.tokenCache = lru({
    max: 50,
    length: function () { return 1; }
  });

  server.userCache = lru({
    max: 50,
    length: function () { return 1; }
  });

  Object.keys(workers).forEach(function createQueue(name) {
    server.queue.process(name, (workers[name].concurrency) ? workers[name].concurrency : 1, function(job, done) {
      job.task = job.data;

      job.anyfetchClient = new Anyfetch(job.task._anyfetchToken);
      job.anyfetchClient.setApiUrl(job.task._anyfetchApiUrl);

      async.waterfall([
        function retrieveToken(cb) {
          if(server.tokenCache.has(job.task._anyfetchToken)) {
            cb(null, server.tokenCache.get(job.task._anyfetchToken));
          }
          else {
            Token.findOne({anyfetchToken: job.task._anyfetchToken}, rarity.slice(2, cb));
          }
        },
        function executeTask(token, cb) {
          if(!token) {
            return cb(new Error("Can't retrieve Token"));
          }

          console.log("User `" + token.accountName + "` is executing task " + job.id + " onto `" + name + "`");

          server.tokenCache.set(job.task._anyfetchToken, token);

          job.serviceData = token.data;
          job.cache = server.userCache;

          // We use task instead of data
          delete job.data;
          delete job.task._anyfetchToken;
          delete job.task._anyfetchApiUrl;

          workers[name](job, cb);
        }
      ], done);
    });
  });

  // Load routes and generate endpoints using closures
  server.get('/', indexEndpoint);
  server.get('/init/connect', initConnectEndpoints(connectFunctions.redirectToService, config));
  server.get('/init/callback', initCallbackEndpoints(connectFunctions.retrieveTokens, config));

  server.post('/update', updateEndpoint(updateAccount, server.queue, workers));

  server.get('/status', statusEndpoint);
  server.del('/reset', resetEndpoint);

  // Expose the server
  return server;
};

module.exports.debug = {
  cleanTokens: function(cb) {
    connectMongo();
    async.parallel([
      function(cb) {
        Token.remove({}, cb);
      },
      function(cb) {
        TempToken.remove({}, cb);
      }
    ], cb);
  },
  createToken: function(hash, cb) {
    connectMongo();

    var token = new Token(hash);
    token.save(cb);
  },
  createTempToken: function(hash, cb) {
    connectMongo();

    var tempToken = new TempToken(hash);
    tempToken.save(cb);
  }
};
<|endoftext|>


<commit_before>var camelize = require('camel-case');

module.exports = function (babel) {
  var t = babel.types;

  function buildElementCall(path, state) {
    path.parent.children = t.react.buildChildren(path.parent);

    var args = [];
    var tagName = parseTagName(path.node.name);

    if (t.isJSXIdentifier(path.node.name) && !/^[A-Z]/.test(tagName)) {
      args.push(t.stringLiteral(tagName));
    } else {
      args.push(toMemberExpression(tagName));
    }

    var attributes = path.node.attributes;
    var attrs = attributes.length ? buildAttributes(attributes, state) : t.objectExpression([]);
    args.push(attrs);

    var h = state.opts.pragma || 'h';

    return t.callExpression(toMemberExpression(h), args);
  }

  function parseTagName(node) {
    if (t.isJSXMemberExpression(node)) {
      if (t.isJSXMemberExpression(node.object)) {
        return parseTagName(node.object) + '.' + node.property.name;
      } else {
        return node.object.name + '.' + node.property.name;
      }
    }
    return node.name;
  }

  function toMemberExpression(expr) {
    return expr.split('.').map(function (name) {
      return t.identifier(name);
    }).reduce(function (object, property) {
      return t.memberExpression(object, property);
    });
  }

  /**
   * The logic for this is quite terse. It's because we need to
   * support spread elements. We loop over all attributes,
   * breaking on spreads, we then push a new object containg
   * all prior attributes to an array for later processing.
   */
  function buildAttributes(attrs, state) {
    var _props = [];
    var objs = [];

    function pushProps() {
      if (!_props.length) return;

      objs.push(t.objectExpression(_props));
      _props = [];
    }

    while (attrs.length) {
      var prop = attrs.shift();
      if (t.isJSXSpreadAttribute(prop)) {
        pushProps();
        objs.push(prop.argument);
      } else {
        _props.push(convertAttribute(prop));
      }
    }

    pushProps();

    if (objs.length === 1) {
      attrs = objs[0];
    } else {
      if (!t.isObjectExpression(objs[0])) {
        objs.unshift(t.objectExpression([]));
      }
      attrs = t.callExpression(
        state.addHelper('extends'),
        objs
      );
    }

    return _buildAttributes(attrs);
  }

  function _buildAttributes(attributes) {
    var result = [];
    var attrsPairs = [];
    var attrsNSPairs = [];
    var eventsPairs = [];
    var dataPairs = [];

    attributes.properties.forEach(function(attr) {
      var name = attr.key.name || attr.key.value;
      if (name === 'xmlns') {
        attrsPairs.push(t.objectProperty(t.identifier('xmlns'), attr.value));
      } else if (name.substr(0, 6) === 'xmlns:') {
        name = name.substr(6);
        attrsNSPairs.push(t.objectProperty(t.stringLiteral(camelize(name)), attr.value));
      } else if (name.substr(0, 3) === 'on-') {
        name = 'on' + name.substr(3);
        eventsPairs.push(t.objectProperty(t.stringLiteral(name), attr.value));
      } else if (name.substr(0, 5) === 'data-') {
        name = name.substr(5);
        dataPairs.push(t.objectProperty(t.stringLiteral(camelize(name)), attr.value));
      } else {
        attrsPairs.push(t.objectProperty(t.stringLiteral(camelize(name)), attr.value));
      }
    });

    if (attrsPairs.length) {
      result.push(t.objectProperty(t.identifier('attrs'), t.objectExpression(attrsPairs)));
    }
    if (attrsNSPairs.length) {
      result.push(t.objectProperty(t.identifier('attrsNS'), t.objectExpression(attrsNSPairs)));
    }
    if (eventsPairs.length) {
      result.push(t.objectProperty(t.identifier('events'), t.objectExpression(eventsPairs)));
    }
    if (dataPairs.length) {
      result.push(t.objectProperty(t.identifier('data'), t.objectExpression(dataPairs)));
    }
    return t.objectExpression(result);
  }

  function convertAttribute(node) {
    var value = convertAttributeValue(node.value || t.booleanLiteral(true));

    if (t.isStringLiteral(value) && !t.isJSXExpressionContainer(node.value)) {
      value.value = value.value.replace(/\n\s+/g, ' ');
    }
    if (t.isJSXNamespacedName(node.name)) {
      node.name = t.stringLiteral(node.name.namespace.name + ':' + node.name.name.name);
    } else if (t.isValidIdentifier(node.name.name)) {
      node.name.type = 'Identifier';
    } else {
      node.name = t.stringLiteral(node.name.name);
    }
    return t.inherits(t.objectProperty(node.name, value), node);
  }

  function convertAttributeValue(node) {
    if (t.isJSXExpressionContainer(node)) {
      return node.expression;
    } else {
      return node;
    }
  }

  var visitor = {
    JSXElement: {
      exit: function(path, state) {
        var expr = buildElementCall(path.get('openingElement'), state);

        if (path.node.children.length) {
          var children = t.arrayExpression();
          children.elements = path.node.children;
          expr.arguments = expr.arguments.concat(children);
        }

        if (expr.arguments.length >= 3) {
          expr._prettyCall = true;
        }

        path.replaceWith(t.inherits(expr, path.node));
      }
    }
    // ,
    // JSXExpressionContainer: {
    //   exit: function(path) {
    //     if (
    //       t.isJSXElement(path.node.expression) ||
    //       t.isJSXEmptyExpression(path.node.expression)
    //     ) {
    //       return;
    //     }
    //     var fct = t.functionExpression(
    //       null,
    //       [],
    //       t.blockStatement(
    //         [
    //           t.tryStatement(
    //             t.blockStatement(
    //               [
    //                 t.returnStatement(
    //                   path.node.expression
    //                 )
    //               ]),
    //             null,
    //             t.blockStatement([])
    //           )
    //         ]
    //       )
    //     );

    //     var jsxExpr = t.jSXExpressionContainer(
    //       t.callExpression(
    //         t.memberExpression(
    //           fct,
    //           t.identifier('call')
    //         ),
    //         [
    //           t.identifier('this')
    //         ]
    //       )
    //     );
    //     path.node.expression = jsxExpr.expression;
    //   }
    // }
  };

  return {
    inherits: require('jsx-control-statements'),
    visitor: visitor
  };
};
<commit_msg>Uses a self invoking function to get the context.<commit_after>var camelize = require('camel-case');

module.exports = function (babel) {
  var t = babel.types;

  function buildElementCall(path, state) {
    path.parent.children = t.react.buildChildren(path.parent);

    var args = [];
    var tagName = parseTagName(path.node.name);

    if (t.isJSXIdentifier(path.node.name) && !/^[A-Z]/.test(tagName)) {
      args.push(t.stringLiteral(tagName));
    } else {
      args.push(toMemberExpression(tagName));
    }

    var attributes = path.node.attributes;
    var attrs = attributes.length ? buildAttributes(attributes, state) : t.objectExpression([]);
    args.push(attrs);

    var h = state.opts.pragma || 'h';

    return t.callExpression(toMemberExpression(h), args);
  }

  function parseTagName(node) {
    if (t.isJSXMemberExpression(node)) {
      if (t.isJSXMemberExpression(node.object)) {
        return parseTagName(node.object) + '.' + node.property.name;
      } else {
        return node.object.name + '.' + node.property.name;
      }
    }
    return node.name;
  }

  function toMemberExpression(expr) {
    return expr.split('.').map(function (name) {
      return t.identifier(name);
    }).reduce(function (object, property) {
      return t.memberExpression(object, property);
    });
  }

  /**
   * The logic for this is quite terse. It's because we need to
   * support spread elements. We loop over all attributes,
   * breaking on spreads, we then push a new object containg
   * all prior attributes to an array for later processing.
   */
  function buildAttributes(attrs, state) {
    var _props = [];
    var objs = [];

    function pushProps() {
      if (!_props.length) return;

      objs.push(t.objectExpression(_props));
      _props = [];
    }

    while (attrs.length) {
      var prop = attrs.shift();
      if (t.isJSXSpreadAttribute(prop)) {
        pushProps();
        objs.push(prop.argument);
      } else {
        _props.push(convertAttribute(prop));
      }
    }

    pushProps();

    if (objs.length === 1) {
      attrs = objs[0];
    } else {
      if (!t.isObjectExpression(objs[0])) {
        objs.unshift(t.objectExpression([]));
      }
      attrs = t.callExpression(
        state.addHelper('extends'),
        objs
      );
    }

    return _buildAttributes(attrs);
  }

  function _buildAttributes(attributes) {
    var result = [];
    var attrsPairs = [];
    var attrsNSPairs = [];
    var eventsPairs = [];
    var dataPairs = [];

    attributes.properties.forEach(function(attr) {
      var name = attr.key.name || attr.key.value;
      if (name === 'xmlns') {
        attrsPairs.push(t.objectProperty(t.identifier('xmlns'), attr.value));
      } else if (name.substr(0, 6) === 'xmlns:') {
        name = name.substr(6);
        attrsNSPairs.push(t.objectProperty(t.stringLiteral(camelize(name)), attr.value));
      } else if (name.substr(0, 3) === 'on-') {
        name = 'on' + name.substr(3);
        eventsPairs.push(t.objectProperty(t.stringLiteral(name), attr.value));
      } else if (name.substr(0, 5) === 'data-') {
        name = name.substr(5);
        dataPairs.push(t.objectProperty(t.stringLiteral(camelize(name)), attr.value));
      } else {
        attrsPairs.push(t.objectProperty(t.stringLiteral(camelize(name)), attr.value));
      }
    });

    if (attrsPairs.length) {
      result.push(t.objectProperty(t.identifier('attrs'), t.objectExpression(attrsPairs)));
    }
    if (attrsNSPairs.length) {
      result.push(t.objectProperty(t.identifier('attrsNS'), t.objectExpression(attrsNSPairs)));
    }
    if (eventsPairs.length) {
      result.push(t.objectProperty(t.identifier('events'), t.objectExpression(eventsPairs)));
    }
    if (dataPairs.length) {
      result.push(t.objectProperty(t.identifier('data'), t.objectExpression(dataPairs)));
    }
    return t.objectExpression(result);
  }

  function convertAttribute(node) {
    var value = convertAttributeValue(node.value || t.booleanLiteral(true));

    if (t.isStringLiteral(value) && !t.isJSXExpressionContainer(node.value)) {
      value.value = value.value.replace(/\n\s+/g, ' ');
    }
    if (t.isJSXNamespacedName(node.name)) {
      node.name = t.stringLiteral(node.name.namespace.name + ':' + node.name.name.name);
    } else if (t.isValidIdentifier(node.name.name)) {
      node.name.type = 'Identifier';
    } else {
      node.name = t.stringLiteral(node.name.name);
    }
    return t.inherits(t.objectProperty(node.name, value), node);
  }

  function convertAttributeValue(node) {
    if (t.isJSXExpressionContainer(node)) {
      return node.expression;
    } else {
      return node;
    }
  }

  var visitor = {
    JSXElement: {
      exit: function(path, state) {
        var expr = buildElementCall(path.get('openingElement'), state);

        var children = t.arrayExpression();
        children.elements = path.node.children;
        expr.arguments = expr.arguments.concat(children, t.identifier('s'));

        if (!t.isJSXElement(path.parent)) {
          var fct = t.functionExpression(
            null,
            [
              t.identifier('s')
            ],
            t.blockStatement(
              [
                t.returnStatement(
                  expr
                )
              ]
            )
          );
          expr = t.callExpression(
            t.memberExpression(
              fct,
              t.identifier('call')
            ),
            [
              t.identifier('this')
            ]
          );
        }

        path.replaceWith(t.inherits(expr, path.node));
      }
    }
    // ,
    // JSXExpressionContainer: {
    //   exit: function(path) {
    //     if (
    //       t.isJSXElement(path.node.expression) ||
    //       t.isJSXEmptyExpression(path.node.expression)
    //     ) {
    //       return;
    //     }
    //     var fct = t.functionExpression(
    //       null,
    //       [],
    //       t.blockStatement(
    //         [
    //           t.tryStatement(
    //             t.blockStatement(
    //               [
    //                 t.returnStatement(
    //                   path.node.expression
    //                 )
    //               ]),
    //             null,
    //             t.blockStatement([])
    //           )
    //         ]
    //       )
    //     );

    //     var jsxExpr = t.jSXExpressionContainer(
    //       t.callExpression(
    //         t.memberExpression(
    //           fct,
    //           t.identifier('call')
    //         ),
    //         [
    //           t.identifier('this')
    //         ]
    //       )
    //     );
    //     path.node.expression = jsxExpr.expression;
    //   }
    // }
  };

  return {
    inherits: require('jsx-control-statements'),
    visitor: visitor
  };
};
<|endoftext|>


<commit_before>/**
 * Module dependencies
 */

var defaults = require('./helpers');
var template = require('./template');

module.exports = createInstance;

/**
 * Create an instance of JSONt
 *
 * @return {JSONt}
 */

function createInstance() {
  function JSONt(tmpl, options){ return JSONt.compile(tmpl, options); };
  JSONt.helpers = {};
  JSONt.helpers.__proto__ = defaults;

  JSONt.use = use;
  JSONt.plugin = plugin;
  JSONt.compile = compile;

  return JSONt;
};

/**
 * Use a helpers
 *
 * @param {String} name
 * @param {Function} fun
 * @return {JSONt} for chaining
 */

function use(name, fun) {
  this.helpers[name] = fun;
  return this;
};

/**
 * Register a collection of helpers
 */

function plugin(fn) {
  fn(this);
  return this;
};

/**
 * Compile a template with the default helpers
 *
 * @param {String|Object} tmpl
 * @param {Object} options
 * @return {Template}
 */

function compile(tmpl, options) {
  return template(tmpl, options || {}, this.helpers);
};
<commit_msg>Add render and renderFile methods<commit_after>/**
 * Module dependencies
 */

var defaults = require('./helpers');
var template = require('./template');

module.exports = createInstance;

/**
 * Create an instance of JSONt
 *
 * @return {JSONt}
 */

function createInstance() {
  function JSONt(tmpl, options){ return JSONt.compile(tmpl, options); };
  JSONt.helpers = {};
  JSONt.helpers.__proto__ = defaults;

  JSONt.use = use;
  JSONt.plugin = plugin;
  JSONt.compile = compile;
  JSONt.render = render;
  JSONt.renderFile = renderFile;

  return JSONt;
};

/**
 * Use a helpers
 *
 * @param {String} name
 * @param {Function} fun
 * @return {JSONt} for chaining
 */

function use(name, fun) {
  this.helpers[name] = fun;
  return this;
};

/**
 * Register a collection of helpers
 */

function plugin(fn) {
  fn(this);
  return this;
};

/**
 * Compile a template with the default helpers
 *
 * @param {String|Object} tmpl
 * @param {Object} options
 * @return {Template}
 */

function compile(tmpl, options) {
  return template(tmpl, options || {}, this.helpers);
};

/**
 * Compile and render a template
 *
 * @param {String|Object} tmpl
 * @param {Object} options
 * @param {Function} fn
 */

function render(tmpl, options, fn) {
  this.compile(tmpl, options)(options, fn);
};

/**
 * Compile and render a template
 *
 * @param {String} filename
 * @param {Object} options
 * @param {Function} fn
 */

function renderFile(file, options, fn) {
  this.compile(require(file), options)(options, fn);
};
<|endoftext|>


<commit_before>var _ = require('underscore'),
    uglifyJs = require('uglify-js'),
    uglifyAst = {};

// JSON subset supported
uglifyAst.objToAst = function (obj) {
    if (obj === null || obj === true || obj === false) {
        return ['name', "" + obj];
    } else if (_.isArray(obj)) {
        return ['array', obj.map(uglifyAst.objToAst)];
    } else if (typeof obj === 'object') {
        return ['object', Object.keys(obj).sort().map(function (key) {
            return [key, uglifyAst.objToAst(obj[key])];
        })];
    } else if (_.isNumber(obj)) {
        return ['num', obj];
    } else if (_.isString(obj)) {
        return ['string', obj];
    } else if (_.isFunction(obj)) {
        var functionAst = uglifyJs.parser.parse('!' + obj.toString())[1][0][1][2];
        if (functionAst[1] === 'anonymous') {
            functionAst[1] = null;
        }
        return functionAst;
    } else if (typeof obj === 'undefined') {
        return ['name', 'undefined'];
    } else {
        throw new Error("uglifyAst.objToAst: Cannot convert " + JSON.stringify(obj));
    }
};

// JSON subset supported
uglifyAst.astToObj = function (ast) {
    if (ast[0] === 'string' || ast[0] === 'num') {
        return ast[1];
    } else if (ast[0] === 'name') {
        if (ast[1] === 'false') {
            return false;
        } else if (ast[1] === 'true') {
            return true;
        } else if (ast[1] === 'null') {
            return null;
        } else if (ast[1] === 'undefined') {
            return undefined;
        } else {
            throw new Error('uglifyAst.astToObj: Unsupported ["name", ...] node');
        }
    } else if (ast[0] === 'object') {
        var obj = {};
        ast[1].forEach(function (keyAndValueArr) {
            obj[keyAndValueArr[0]] = uglifyAst.astToObj(keyAndValueArr[1]);
        });
        return obj;
    } else if (ast[0] === 'array') {
        return ast[1].map(uglifyAst.astToObj);
    } else if (ast[0] === 'function') {
        return new Function(ast[2].join(","), uglifyJs.uglify.gen_code(['toplevel', ast[3]]));
    } else {
        throw new Error("uglifyAst.astToObj: Cannot convert " + JSON.stringify(ast));
    }
};

uglifyAst.getFunctionBodyAst = function (lambda) {
    return uglifyJs.parser.parse("(" + lambda.toString() + ")")[1][0][1][3];
};

var astNodeTypes = ['array', 'object'];

// Extracts code string => occurrences hash
uglifyAst.findOccurrencesByCode = function (ast) {
    var walker = uglifyJs.uglify.ast_walker(),
        occurrencesByCode = {},
        walkerConfig = {};
    astNodeTypes.forEach(function (astNodeType) {
        walkerConfig[astNodeType] = function () {
            var code = uglifyJs.uglify.gen_code(this);
            if (code.length > 20) {
                (occurrencesByCode[code] = occurrencesByCode[code] || []).push({node: this, stack: [].concat(walker.stack())});
            }
        };
    });

    walker.with_walkers(walkerConfig, function () {
        walker.walk(ast);
    });
    return occurrencesByCode;
};

// Assumes that all nodes in the ast are "const", adds var declaration at top. The 'ast' parameter must be a ['toplevel', ...] node.
uglifyAst.pullCommonStructuresIntoVars = function (ast, varNamePrefix) {
    varNamePrefix = varNamePrefix || '_' + (Math.floor(1000000 * Math.random())).toString(36);

    var occurrencesByCode = uglifyAst.findOccurrencesByCode(ast),
        varDeclarations = [],
        nextVarNumber = 1;
    Object.keys(occurrencesByCode).sort(function (a, b) {
        return a.length - b.length;
    }).forEach(function (code) {
        var occurrences = [].concat(occurrencesByCode[code]);
        if (occurrences.length > 1 && occurrences.length * code.length > 10) {
            for (var i = 1 ; i < occurrences.length ; i += 1) {
                var occurrence = occurrences[i],
                    walkerConfig = {};
                astNodeTypes.forEach(function (astNodeType) {
                    walkerConfig[astNodeType] = function () {
                        var occurrencesOfThisCode = occurrencesByCode[uglifyJs.uglify.gen_code(this)];
                        if (occurrencesOfThisCode) {
                            var indexOfThisOccurrence = occurrencesOfThisCode.indexOf(this);
                            if (indexOfThisOccurrence !== -1) {
                                occurrencesOfThisCode.splice(indexOfThisOccurrence, 1);
                            }
                        }
                    };
                });
                var walker = uglifyJs.uglify.ast_walker();
                walker.with_walkers(walkerConfig, function () {
                    walker.walk(occurrence.node);
                });
            }
            var varName = varNamePrefix + nextVarNumber;
            nextVarNumber += 1;
            varDeclarations.push([varName, [].concat(occurrences[0].node)]);
            occurrences.forEach(function (occurrence) {
                occurrence.node.splice(0, occurrence.node.length, 'name', varName);
            });
        }
    });
    ast[1].unshift(['var', varDeclarations]);
};

uglifyAst.foldConstant = function (ast) {
    if (ast[0] === 'string') {
        return ast;
    }
    return uglifyJs.uglify.ast_squeeze(['toplevel', [['var', [['__bogus', ast]]]]])[1][0][1][0][1];
};

uglifyAst.parseExpression = function (str) {
    return uglifyJs.parser.parse('var _bogus = (' + str + ')')[1][0][1][0][1];
};

_.extend(exports, uglifyAst);
<commit_msg>Pulled more code into separate methods.<commit_after>var _ = require('underscore'),
    uglifyJs = require('uglify-js'),
    uglifyAst = {};

// JSON subset supported
uglifyAst.objToAst = function (obj) {
    if (obj === null || obj === true || obj === false) {
        return ['name', "" + obj];
    } else if (_.isArray(obj)) {
        return ['array', obj.map(uglifyAst.objToAst)];
    } else if (typeof obj === 'object') {
        return ['object', Object.keys(obj).sort().map(function (key) {
            return [key, uglifyAst.objToAst(obj[key])];
        })];
    } else if (_.isNumber(obj)) {
        return ['num', obj];
    } else if (_.isString(obj)) {
        return ['string', obj];
    } else if (_.isFunction(obj)) {
        var functionAst = uglifyJs.parser.parse('!' + obj.toString())[1][0][1][2];
        if (functionAst[1] === 'anonymous') {
            functionAst[1] = null;
        }
        return functionAst;
    } else if (typeof obj === 'undefined') {
        return ['name', 'undefined'];
    } else {
        throw new Error("uglifyAst.objToAst: Cannot convert " + JSON.stringify(obj));
    }
};

// JSON subset supported
uglifyAst.astToObj = function (ast) {
    if (ast[0] === 'string' || ast[0] === 'num') {
        return ast[1];
    } else if (ast[0] === 'name') {
        if (ast[1] === 'false') {
            return false;
        } else if (ast[1] === 'true') {
            return true;
        } else if (ast[1] === 'null') {
            return null;
        } else if (ast[1] === 'undefined') {
            return undefined;
        } else {
            throw new Error('uglifyAst.astToObj: Unsupported ["name", ...] node');
        }
    } else if (ast[0] === 'object') {
        var obj = {};
        ast[1].forEach(function (keyAndValueArr) {
            obj[keyAndValueArr[0]] = uglifyAst.astToObj(keyAndValueArr[1]);
        });
        return obj;
    } else if (ast[0] === 'array') {
        return ast[1].map(uglifyAst.astToObj);
    } else if (ast[0] === 'function') {
        return new Function(ast[2].join(","), uglifyJs.uglify.gen_code(['toplevel', ast[3]]));
    } else {
        throw new Error("uglifyAst.astToObj: Cannot convert " + JSON.stringify(ast));
    }
};

uglifyAst.getFunctionBodyAst = function (lambda) {
    return uglifyJs.parser.parse("(" + lambda.toString() + ")")[1][0][1][3];
};

var astNodeTypes = ['array', 'object'];

// Extracts code string => occurrences hash
uglifyAst.findOccurrencesByCode = function (ast) {
    var walker = uglifyJs.uglify.ast_walker(),
        occurrencesByCode = {},
        walkerConfig = {};
    astNodeTypes.forEach(function (astNodeType) {
        walkerConfig[astNodeType] = function () {
            var code = uglifyJs.uglify.gen_code(this);
            if (code.length > 20) {
                (occurrencesByCode[code] = occurrencesByCode[code] || []).push({node: this, stack: [].concat(walker.stack())});
            }
        };
    });

    walker.with_walkers(walkerConfig, function () {
        walker.walk(ast);
    });
    return occurrencesByCode;
};

uglifyAst.iterateOverOccurrences = function (occurrencesByCode, lambda) {
    Object.keys(occurrencesByCode).sort(function (a, b) {
        return a.length - b.length;
    }).forEach(function (code) {
        var occurrences = [].concat(occurrencesByCode[code]);
        if (lambda(code, occurrences) !== false) {
            for (var i = 1 ; i < occurrences.length ; i += 1) {
                var occurrence = occurrences[i],
                    walkerConfig = {};
                astNodeTypes.forEach(function (astNodeType) {
                    walkerConfig[astNodeType] = function () {
                        var occurrencesOfThisCode = occurrencesByCode[uglifyJs.uglify.gen_code(this)];
                        if (occurrencesOfThisCode) {
                            var indexOfThisOccurrence = occurrencesOfThisCode.indexOf(this);
                            if (indexOfThisOccurrence !== -1) {
                                occurrencesOfThisCode.splice(indexOfThisOccurrence, 1);
                            }
                        }
                    };
                });
                var walker = uglifyJs.uglify.ast_walker();
                walker.with_walkers(walkerConfig, function () {
                    walker.walk(occurrence.node);
                });
                }
        }
    });
};

// Assumes that all nodes in the ast are "const", adds var declaration at top. The 'ast' parameter must be a ['toplevel', ...] node.
uglifyAst.pullCommonStructuresIntoVars = function (ast, varNamePrefix) {
    varNamePrefix = varNamePrefix || '_' + (Math.floor(1000000 * Math.random())).toString(36);

    var occurrencesByCode = uglifyAst.findOccurrencesByCode(ast),
        varDeclarations = [],
        nextVarNumber = 1;

    uglifyAst.iterateOverOccurrences(occurrencesByCode, function (code, occurrences) {
        if (occurrences.length > 1 && occurrences.length * code.length > 10) {
            var varName = varNamePrefix + nextVarNumber;
            nextVarNumber += 1;
            varDeclarations.push([varName, [].concat(occurrences[0].node)]);
            occurrences.forEach(function (occurrence) {
                occurrence.node.splice(0, occurrence.node.length, 'name', varName);
            });
        } else {
            return false;
        }
    });
    ast[1].unshift(['var', varDeclarations]);
};

uglifyAst.foldConstant = function (ast) {
    if (ast[0] === 'string') {
        return ast;
    }
    return uglifyJs.uglify.ast_squeeze(['toplevel', [['var', [['__bogus', ast]]]]])[1][0][1][0][1];
};

uglifyAst.parseExpression = function (str) {
    return uglifyJs.parser.parse('var _bogus = (' + str + ')')[1][0][1][0][1];
};

_.extend(exports, uglifyAst);
<|endoftext|>


<commit_before>'use strict';
/**
 * SmartSlack is a node.js module for Slack's Real Time Messaging API
 * @copyright Phillip J. Henslee II 2015 <ph2@ph2.us>
 * @module smartslack
 */

var EventEmitter = require('events').EventEmitter;
var bole = require('bole');
var dns = require('dns');
var https = require('https');
var _ = require('lodash');
var util = require('util');
var WebSocket = require('ws');

var Attachment = require('./slack/attachment');
var Cache = require('./cache');
var errors = require('./errors');
var logging = require('./logging');
var slack = require('./slack/');
var common = require('./common');

/**
 * Slack RTM API Client
 * @constructor
 * @param {object} options required configuration options
 */
function SmartSlack(options) {

    // Must provide a valid options argument
    if (!_.isObject(options)) {
        throw new Error(errors.missing_options_arg);
    }

    var accessToken = options.token;

    // Need a valid Slack token to authenticate
    if (typeof accessToken !== 'string' || !accessToken.match(/^([a-z]{4})-([0-9]{11,12})-([0-9a-zA-Z]{24})$/)) {
        throw new Error(errors.invalid_token);
    }

    // Save the cache data
    Cache.defaults(options);
    Cache.add({hostname: 'slack.com'});

    // API references
    this.channels = slack.channels;
    this.chat = slack.chat;
    this.emoji = slack.emoji;
    this.groups = slack.groups;
    this.im = slack.im;
    this.reactions = slack.reactions;
    this.test = slack.test;
    this.users = slack.users;

    // Logger
    this.log = bole('SmartSlack');

    this._autoReconnect = options.autoReconnect || true;
    this._lastPong = null;
    this._messageId = 0;
    this._socketUrl = null;
    this._webSocket = null;
}

util.inherits(SmartSlack, EventEmitter);

/**
 * Create a new attachment object
 * @param {string} text The text of the attachment
 * @returns {object} Attachment The attachment instance
 */
SmartSlack.prototype.createAttachment = function (text) {
    return new Attachment(text);
};

/**
 * Start the RTM session
 */
SmartSlack.prototype.start = function () {
    var _this = this;
    slack.rtm.start(function (err, result) {

        if (err) {
            throw err;
        }

        _this.log.info('Slack RTM session started...');
        _this.log.debug(result);

        _this._startTime = _.now();
        _this.name = result.self.name;
        _this.id = result.self.id;

        // Cache the Slack session data
        Cache.data.user = result.self;
        Cache.data.users = result.users;
        Cache.data.channels = result.channels;
        Cache.data.groups = result.groups;
        Cache.data.ims = result.ims;
        Cache.data.team = result.team;

        // Valid for thirty seconds...
        _this._socketUrl = result.url;

        // Connect web socket
        _this._connectSocket();
    });
};

/**
 * Post a direct message via the API
 * @description Convenience function so you can call
 * client.postDirectMessage instead of client.chat.postDirectMessage;
 * @param {string} user The user id, name, or email address
 * @param {string} text The message text
 * @param {object} options Additional argument options for the message
 * @param {function} callback(err,result)
 */
SmartSlack.prototype.postDirectMessage = function (user, text, options, callback) {
    if (typeof options === 'function') {
        callback = options;
        options = null;
    }
    callback = (_.isFunction(callback)) ? callback : _.noop;

    if (!_.isString(user) && !_.isString(text)) {
        return callback(new Error(errors.missing_required_arg));
    }
    return slack.chat.postDirectMessage(user, text, options, callback);

};

/**
 * Post a message via the API
 * @description Convenience function so you can call
 * client.postMessage instead of client.chat.postMessage;
 * @param {string} channel The channel name or id
 * @param {string} text The message text
 * @param {object} options Additional argument options for the message
 * @param {function} callback(err,result)
 */
SmartSlack.prototype.postMessage = function (channel, text, options, callback) {
    if (typeof options === 'function') {
        callback = options;
        options = null;
    }
    callback = (_.isFunction(callback)) ? callback : _.noop;

    if (!_.isString(channel) && !_.isString(text)) {
        return callback(new Error(errors.missing_required_arg));
    }
    return slack.chat.postMessage(channel, text, options, callback);
};

/**
 * Send RTM message to channel
 * @param {string} channel The channel name (i.e. general)
 * @param {string} text The message text
 * @param {function} callback(err,result)
 */
SmartSlack.prototype.sendToChannel = function (channel, text, callback) {
    callback = (_.isFunction(callback)) ? callback : _.noop;

    if (!_.isString(channel) && !_.isString(text)) {
        return callback(new Error(errors.missing_required_arg));
    }
    if (channel.match(/^([C]0)/)) {
        return this.send(channel, text);
    }
    return this._sendToType(slack.types.CHANNEL, channel, text, callback);
};

/**
 * Send RTM message to group
 * @param {string} group The private group name
 * @param {string} text The message text
 * @param {function} callback(err,result)
 */
SmartSlack.prototype.sendToGroup = function (group, text, callback) {
    callback = (_.isFunction(callback)) ? callback : _.noop;

    if (!_.isString(group) && !_.isString(text)) {
        return callback(new Error(errors.missing_required_arg));
    }
    if (group.match(/^([G]0)/)) {
        return this.send(group, text);
    }
    return this._sendToType(slack.types.GROUP, group, text, callback);
};

/**
 * Send RTM message to user
 * @param {string} user The user id, name, email address
 * @param {string} text The message text
 * @param {function} callback(err,result)
 */
SmartSlack.prototype.sendToUser = function (user, text, callback) {
    callback = (_.isFunction(callback)) ? callback : _.noop;

    if (!_.isString(user) && !_.isString(text)) {
        return callback(new Error(errors.missing_required_arg));
    }
    if (user.match(/^([D]0)/)) {
        return this.send(user, text);
    }
    return this._sendToType(slack.types.USER, user, text, callback);
};

/**
 * Used for reconnection, attempts to resolve
 * slack.com calls login() if resolved
 */
SmartSlack.prototype._canResolve = function () {

    var _this = this;

    this.log.debug('Attempting to resolve ' + Cache.data.hostname);

    dns.resolve4(Cache.data.hostname, function (err, addresses) {

        if (err) {
            _this.log.error('DNS resolution failed. Error Code: ' + err.code);
        } else {

            _this.log.debug('Resolved ' + Cache.data.hostname + ' (' + addresses[0] + ')');

            if (_this._reconnecting) {
                clearInterval(_this._reconnecting);
                _this.start();
            }
        }
    });
};

/**
 * Connect webSocket
 * @access private
 */
SmartSlack.prototype._connectSocket = function () {

    var _this = this;
    var output;

    this._webSocket = new WebSocket(this._socketUrl);

    this._webSocket.on('open', function (result) {

        _this.emit('open', result);
        _this._lastPong = _.now();
    });

    this._webSocket.on('close', function (result) {

        _this.emit('close', result);
        if (_this._autoReconnect) {
            _this._reconnect();
        }
    });

    this._webSocket.on('error', function (result) {

        _this.emit('error', result);
    });

    this._webSocket.on('message', function (result) {

        try {
            output = JSON.parse(result);
            _this._onRtmEvent(output);
        } catch (error) {
            _this.emit('error', error);
        }
    });
};

/**
 * Handle slack RTM event message
 * @param {object} message The Slack RTM JSON event message
 * @access private
 */
SmartSlack.prototype._onRtmEvent = function (message) {

    var _this = this;

    this.emit('eventmessage', message);
    this.log.debug(message);

    switch (message.type) {

        case 'hello':
            // Received the hello event message
            this.log.info('Slack hello received, socket connected...');
            this.emit('connected');
            this._lastPong = _.now();

            // Start pings every five seconds
            this._pingInterval = setInterval(function () {
                _this._ping();
            }, 5000);
            break;

        case 'message':
            this.emit('message', message);
            break;

        case 'pong':
            this._lastPong = _.now();
            this.log.debug('Last pong latency: ' + (this._lastPong - message.time) + 'ms');
            break;

        case 'presence_change':
            _.find(Cache.data.users, {id: message.user}).presence = message.presence;
            this.log.info('User presence changed, updating user presence...');
            break;

        case 'team_join event':
            this.log.info('New member joined the team, updating user list...');
            this.users.getList(function (err, result) {
                if (err) {
                    _this.log.error(err);
                } else {
                    Cache.data.users = result.members;
                }
            });
            break;

        case 'user_change':
            this.log.info('User information has changed, updating user list...');
            this.users.getList(function (err, result) {
                if (err) {
                    _this.log.error(err);
                } else {
                    Cache.data.users = result.members;
                }
            });
            break;
    }
};

/**
 * Sends ping message over the RTM
 * @access private
 */
SmartSlack.prototype._ping = function () {
    var _this = this;
    var result;
    var ping = {type: "ping"};
    ping.id = _this._messageId;
    ping.time = _.now();
    result = JSON.stringify(ping);
    _this._webSocket.send(result);
    _this._messageId += 1;
};

/**
 * Reconnects to Slack RTM, called from canResolve()
 * @access private
 */
SmartSlack.prototype._reconnect = function () {

    var _this = this;

    // Connection lost stop pinging and reset
    clearInterval(_this._pingInterval);
    _this._lastPong = null;
    _this._startTime = null;

    //  Attempt to resolve slack.com and call login when available
    _this._reconnecting = setInterval(function () {
        _this._canResolve();
    }, 5000);
    _this.log.info('Connection lost, waiting to reconnect...');
};

/**
 * Sends a message via the RTM socket
 * @param {string} channel The channel id
 * @param {string} text The message text
 * @access private
 */
SmartSlack.prototype.send = function (channel, text) {
    var _this = this;
    var message;
    var data;

    text = common.escape(text);

    message = {
        id: this._messageId,
        type: 'message',
        channel: channel,
        text: text
    };

    try {
        data = JSON.stringify(message);
    } catch (error) {
        this.log.error(error);
    }

    _this._webSocket.send(data);
    _this._messageId += 1;

};

/**
 * Send a RTM socket message to channel, group, or user
 * @param slackType {string} enum slackTypes.CHANNEL
 * @param typeName {string} the entity's name
 * @param text {string} the message text
 * @param {function} callback(err,result)
 */
SmartSlack.prototype._sendToType = function (slackType, typeName, text, callback) {

    var entityId;

    callback = (_.isFunction(callback)) ? callback : _.noop;

    switch (slackType) {
        case slack.types.CHANNEL:
            this.channels.getChannel(typeName, function (err, result) {
                if (!err) {
                    entityId = result.id;
                }
            });
            break;
        case slack.types.GROUP:
            this.groups.getGroup(typeName, function (err, result) {
                if (!err) {
                    entityId = result.id;
                }
            });
            break;
        case slack.types.USER:
            this.users.getImChannel(typeName, function (err, result) {
                if (!err) {
                    entityId = result;
                }
            });
            break;
        default:
            entityId = null;

    }

    if (entityId) {
        return this.send(entityId, text);
    }
    callback(new TypeError(errors.invalid_entity_type));
};

module.exports = SmartSlack;
<commit_msg>New token format<commit_after>'use strict';
/**
 * SmartSlack is a node.js module for Slack's Real Time Messaging API
 * @copyright Phillip J. Henslee II 2015 <ph2@ph2.us>
 * @module smartslack
 */

var EventEmitter = require('events').EventEmitter;
var bole = require('bole');
var dns = require('dns');
var https = require('https');
var _ = require('lodash');
var util = require('util');
var WebSocket = require('ws');

var Attachment = require('./slack/attachment');
var Cache = require('./cache');
var errors = require('./errors');
var logging = require('./logging');
var slack = require('./slack/');
var common = require('./common');

/**
 * Slack RTM API Client
 * @constructor
 * @param {object} options required configuration options
 */
function SmartSlack(options) {

    // Must provide a valid options argument
    if (!_.isObject(options)) {
        throw new Error(errors.missing_options_arg);
    }

    var accessToken = options.token;

    // Need a valid Slack token to authenticate
    // Updated on July 29, 2019 to accommodate new token format.
    // Reference: https://api.slack.com/docs/oauth
    if (typeof accessToken !== 'string' || !accessToken.match(/^([a-z]{4})-([0-9]{11,12})-([0-9a-zA-Z]{24})$/)
        && !accessToken.match(/^([a-z]{4})-([0-9]{10,11})-([0-9]{11,12})-([0-9a-zA-Z]{24})$/)) {
        throw new Error(errors.invalid_token);
    }

    // Save the cache data
    Cache.defaults(options);
    Cache.add({hostname: 'slack.com'});

    // API references
    this.channels = slack.channels;
    this.chat = slack.chat;
    this.emoji = slack.emoji;
    this.groups = slack.groups;
    this.im = slack.im;
    this.reactions = slack.reactions;
    this.test = slack.test;
    this.users = slack.users;

    // Logger
    this.log = bole('SmartSlack');

    this._autoReconnect = options.autoReconnect || true;
    this._lastPong = null;
    this._messageId = 0;
    this._socketUrl = null;
    this._webSocket = null;
}

util.inherits(SmartSlack, EventEmitter);

/**
 * Create a new attachment object
 * @param {string} text The text of the attachment
 * @returns {object} Attachment The attachment instance
 */
SmartSlack.prototype.createAttachment = function (text) {
    return new Attachment(text);
};

/**
 * Start the RTM session
 */
SmartSlack.prototype.start = function () {
    var _this = this;
    slack.rtm.start(function (err, result) {

        if (err) {
           throw err;
        }

        _this.log.info('Slack RTM session started...');
        _this.log.debug(result);

        _this._startTime = _.now();
        _this.name = result.self.name;
        _this.id = result.self.id;

        // Cache the Slack session data
        Cache.data.user = result.self;
        Cache.data.users = result.users;
        Cache.data.channels = result.channels;
        Cache.data.groups = result.groups;
        Cache.data.ims = result.ims;
        Cache.data.team = result.team;

        // Valid for thirty seconds...
        _this._socketUrl = result.url;

        // Connect web socket
        _this._connectSocket();
    });
};

/**
 * Post a direct message via the API
 * @description Convenience function so you can call
 * client.postDirectMessage instead of client.chat.postDirectMessage;
 * @param {string} user The user id, name, or email address
 * @param {string} text The message text
 * @param {object} options Additional argument options for the message
 * @param {function} callback(err,result)
 */
SmartSlack.prototype.postDirectMessage = function (user, text, options, callback) {
    if (typeof options === 'function') {
        callback = options;
        options = null;
    }
    callback = (_.isFunction(callback)) ? callback : _.noop;

    if (!_.isString(user) && !_.isString(text)) {
        return callback(new Error(errors.missing_required_arg));
    }
    return slack.chat.postDirectMessage(user, text, options, callback);

};

/**
 * Post a message via the API
 * @description Convenience function so you can call
 * client.postMessage instead of client.chat.postMessage;
 * @param {string} channel The channel name or id
 * @param {string} text The message text
 * @param {object} options Additional argument options for the message
 * @param {function} callback(err,result)
 */
SmartSlack.prototype.postMessage = function (channel, text, options, callback) {
    if (typeof options === 'function') {
        callback = options;
        options = null;
    }
    callback = (_.isFunction(callback)) ? callback : _.noop;

    if (!_.isString(channel) && !_.isString(text)) {
        return callback(new Error(errors.missing_required_arg));
    }
    return slack.chat.postMessage(channel, text, options, callback);
};

/**
 * Send RTM message to channel
 * @param {string} channel The channel name (i.e. general)
 * @param {string} text The message text
 * @param {function} callback(err,result)
 */
SmartSlack.prototype.sendToChannel = function (channel, text, callback) {
    callback = (_.isFunction(callback)) ? callback : _.noop;

    if (!_.isString(channel) && !_.isString(text)) {
        return callback(new Error(errors.missing_required_arg));
    }
    if (channel.match(/^([C]0)/)) {
        return this.send(channel, text);
    }
    return this._sendToType(slack.types.CHANNEL, channel, text, callback);
};

/**
 * Send RTM message to group
 * @param {string} group The private group name
 * @param {string} text The message text
 * @param {function} callback(err,result)
 */
SmartSlack.prototype.sendToGroup = function (group, text, callback) {
    callback = (_.isFunction(callback)) ? callback : _.noop;

    if (!_.isString(group) && !_.isString(text)) {
        return callback(new Error(errors.missing_required_arg));
    }
    if (group.match(/^([G]0)/)) {
        return this.send(group, text);
    }
    return this._sendToType(slack.types.GROUP, group, text, callback);
};

/**
 * Send RTM message to user
 * @param {string} user The user id, name, email address
 * @param {string} text The message text
 * @param {function} callback(err,result)
 */
SmartSlack.prototype.sendToUser = function (user, text, callback) {
    callback = (_.isFunction(callback)) ? callback : _.noop;

    if (!_.isString(user) && !_.isString(text)) {
        return callback(new Error(errors.missing_required_arg));
    }
    if (user.match(/^([D]0)/)) {
        return this.send(user, text);
    }
    return this._sendToType(slack.types.USER, user, text, callback);
};

/**
 * Used for reconnection, attempts to resolve
 * slack.com calls login() if resolved
 */
SmartSlack.prototype._canResolve = function () {

    var _this = this;

    this.log.debug('Attempting to resolve ' + Cache.data.hostname);

    dns.resolve4(Cache.data.hostname, function (err, addresses) {

        if (err) {
            _this.log.error('DNS resolution failed. Error Code: ' + err.code);
        } else {

            _this.log.debug('Resolved ' + Cache.data.hostname + ' (' + addresses[0] + ')');

            if (_this._reconnecting) {
                clearInterval(_this._reconnecting);
                _this.start();
            }
        }
    });
};

/**
 * Connect webSocket
 * @access private
 */
SmartSlack.prototype._connectSocket = function () {

    var _this = this;
    var output;

    this._webSocket = new WebSocket(this._socketUrl);

    this._webSocket.on('open', function (result) {

        _this.emit('open', result);
        _this._lastPong = _.now();
    });

    this._webSocket.on('close', function (result) {

        _this.emit('close', result);
        if (_this._autoReconnect) {
            _this._reconnect();
        }
    });

    this._webSocket.on('error', function (result) {

        _this.emit('error', result);
    });

    this._webSocket.on('message', function (result) {

        try {
            output = JSON.parse(result);
            _this._onRtmEvent(output);
        } catch (error) {
            _this.emit('error', error);
        }
    });
};

/**
 * Handle slack RTM event message
 * @param {object} message The Slack RTM JSON event message
 * @access private
 */
SmartSlack.prototype._onRtmEvent = function (message) {

    var _this = this;

    this.emit('eventmessage', message);
    this.log.debug(message);

    switch (message.type) {

        case 'hello':
            // Received the hello event message
            this.log.info('Slack hello received, socket connected...');
            this.emit('connected');
            this._lastPong = _.now();

            // Start pings every five seconds
            this._pingInterval = setInterval(function () {
                _this._ping();
            }, 5000);
            break;

        case 'message':
            this.emit('message', message);
            break;

        case 'pong':
            this._lastPong = _.now();
            this.log.debug('Last pong latency: ' + (this._lastPong - message.time) + 'ms');
            break;

        case 'presence_change':
            _.find(Cache.data.users, {id: message.user}).presence = message.presence;
            this.log.info('User presence changed, updating user presence...');
            break;

        case 'team_join event':
            this.log.info('New member joined the team, updating user list...');
            this.users.getList(function (err, result) {
                if (err) {
                    _this.log.error(err);
                } else {
                    Cache.data.users = result.members;
                }
            });
            break;

        case 'user_change':
            this.log.info('User information has changed, updating user list...');
            this.users.getList(function (err, result) {
                if (err) {
                    _this.log.error(err);
                } else {
                    Cache.data.users = result.members;
                }
            });
            break;
    }
};

/**
 * Sends ping message over the RTM
 * @access private
 */
SmartSlack.prototype._ping = function () {
    var _this = this;
    var result;
    var ping = {type: "ping"};
    ping.id = _this._messageId;
    ping.time = _.now();
    result = JSON.stringify(ping);
    _this._webSocket.send(result);
    _this._messageId += 1;
};

/**
 * Reconnects to Slack RTM, called from canResolve()
 * @access private
 */
SmartSlack.prototype._reconnect = function () {

    var _this = this;

    // Connection lost stop pinging and reset
    clearInterval(_this._pingInterval);
    _this._lastPong = null;
    _this._startTime = null;

    //  Attempt to resolve slack.com and call login when available
    _this._reconnecting = setInterval(function () {
        _this._canResolve();
    }, 5000);
    _this.log.info('Connection lost, waiting to reconnect...');
};

/**
 * Sends a message via the RTM socket
 * @param {string} channel The channel id
 * @param {string} text The message text
 * @access private
 */
SmartSlack.prototype.send = function (channel, text) {
    var _this = this;
    var message;
    var data;

    text = common.escape(text);

    message = {
        id: this._messageId,
        type: 'message',
        channel: channel,
        text: text
    };

    try {
        data = JSON.stringify(message);
    } catch (error) {
        this.log.error(error);
    }

    _this._webSocket.send(data);
    _this._messageId += 1;

};

/**
 * Send a RTM socket message to channel, group, or user
 * @param slackType {string} enum slackTypes.CHANNEL
 * @param typeName {string} the entity's name
 * @param text {string} the message text
 * @param {function} callback(err,result)
 */
SmartSlack.prototype._sendToType = function (slackType, typeName, text, callback) {

    var entityId;

    callback = (_.isFunction(callback)) ? callback : _.noop;

    switch (slackType) {
        case slack.types.CHANNEL:
            this.channels.getChannel(typeName, function (err, result) {
                if (!err) {
                    entityId = result.id;
                }
            });
            break;
        case slack.types.GROUP:
            this.groups.getGroup(typeName, function (err, result) {
                if (!err) {
                    entityId = result.id;
                }
            });
            break;
        case slack.types.USER:
            this.users.getImChannel(typeName, function (err, result) {
                if (!err) {
                    entityId = result;
                }
            });
            break;
        default:
            entityId = null;

    }

    if (entityId) {
        return this.send(entityId, text);
    }
    callback(new TypeError(errors.invalid_entity_type));
};

module.exports = SmartSlack;
<|endoftext|>