<commit_before>/**
 * nengine
 * https://nuintun.github.io/nengine
 *
 * Licensed under the MIT license
 * https://github.com/Nuintun/nengine/blob/master/LICENSE
 */

'use strict';

var fs = require('fs'),
    cwd = process.cwd(),
    dirname = __dirname,
    path = require('path'),
    http = require('http'),
    mix = require('./mix'),
    send = require('./send'),
    log4js = require('log4js'),
    pkg = require('../package.json'),
    nativeRes = require('../native-assets');

/**
 * File send
 * @param requset
 * @param response
 * @param config
 * @returns {SendStream|*|exports}
 */
function fileSend(requset, response, config){
    var that = this,
        steam = send(requset, response, config);

    // Directory
    steam.on('directory', function (fpath){
        switch (config.directory) {
            case 'allow':
                viewFolder.call(that, this, fpath);
                break;
            case 'deny':
                return steam.error(403);
            case 'ignore':
            default:
                return steam.error(404);
        }
    });

    // Error
    steam.on('error', function (err){
        httpError.call(that, this, err);
    });

    // Send
    steam.pipe();

    return steam;
}

/**
 * Error status
 * @param send
 * @param err
 */
function defaultStatus(send, err){
    var message,
        that = this,
        requset = send.requset,
        response = send.response;

    // Not found
    if (err.status === 404) {
        // Not found favicon.ico use native ico
        if (requset.url === '/favicon.ico') {
            response.end();

            that.logger.warn('Request: /favicon.ico Not Found');

            fs.existsSync(path.join(send.root, that.favicon), function (){
                requset.url = that.favicon;

                fileSend.call(that, requset, response, that.config);
            });
        } else {
            message = 'Not Found';

            that.logger.warn('Request: ' + requset.url + ' ' + message);

            response.statusCode = 404;
            response.setHeader('Content-Type', 'text/html');
            response.end(that.nativeRes.html['404']);
        }
    } else {
        // Server error
        message = err.message || 'Nengine Server Error';

        that.logger.warn('Request: ' + requset.url + ' ' + message);

        response.statusCode = err.status || 500;
        response.setHeader('Content-Type', 'text/html');
        response.end(that.nativeRes.html['default'](response.statusCode, message));
    }
}

/**
 * Server error
 * @param send
 * @param err
 */
function httpError(send, err){
    var that = this,
        config = that.config,
        status = err.status,
        requset = send.requset,
        response = send.response;

    response.statusCode = status;

    if (config.status[status]) {
        response.end();

        fs.existsSync(path.join(send.root, that.favicon), function (){
            requset.url = config.status[status];

            fileSend.call(that, requset, response, that.config);
        });
    } else {
        defaultStatus.call(that, send, err);
    }
}

/**
 * Format to http style
 * @param path
 * @returns {XML|*|string|void}
 */
function httpPath(path){
    return path.replace(/\\/g, '/');
}

/**
 * View folder
 * @param send
 * @param fpath
 */
function viewFolder(send, fpath){
    var that = this,
        dirpath = send.path,
        response = send.response;

    fs.readdir(fpath, function (err, files){
        response.end(that.nativeRes.html['folder'](dirpath, files));
    });
}

/**
 * NengineServer
 * @param options
 * @returns {*}
 * @constructor
 */
function NengineServer(options){
    var logs, logger,
        config = mix({}, options);

    // Format params
    config.root = config.root || cwd;
    config.port = config.port || 80;
    config.directory = config.directory || 'deny';
    config.directory = config.directory.toLowerCase();
    config.status = config.status || {};
    this.nativeRes = nativeRes(config.root);

    // Make log directory
    logs = path.join(config.root, 'logs');

    if (!fs.existsSync(logs)) {
        try {
            fs.mkdirSync(logs);
        } catch (e) {
            return config.log('Please create ".Logs" directory in the root directory');
        }
    }

    // Configure log4js
    log4js.configure({
        appenders: [
            {
                type: 'console'
            },
            {
                type: 'file',
                maxLogSize: 20480,
                filename: path.join(logs, 'nengine.log'),
                category: 'Nengine'
            }
        ]
    });

    // Get logger
    logger = log4js.getLogger('Nengine');
    logger.setLevel('ALL');

    this.logger = logger;
    this.config = config;
    this.pathToRoot = '/' + httpPath(path.relative(config.root, path.dirname(dirname)));
    this.favicon = httpPath(path.join(this.pathToRoot, 'favicon.ico'));

    return this;
}

NengineServer.prototype = {
    run: function (){
        var that = this,
            config = that.config,
            server = config.server;

        // Create server
        var httpServer = http.createServer(function (requset, response){
            if (server) {
                server = server === true ? 'Nengine' + (pkg.version ? '/' + pkg.version : '') : server;
                typeof server === 'string' && response.setHeader('Server', server);
            }

            // Send file
            fileSend.call(that, requset, response, config);
        });

        // Start listening
        httpServer.on('listening', function (){
            that.logger.info('Server runing at port: ' + config.port);
        });

        // Error
        httpServer.on('error', function (err){
            that.logger.error('Server failed to start: ' + err.message);
        });

        // Close
        httpServer.on('close', function (){
            that.logger.info('Server closed');
        });

        // Listen
        httpServer.listen(config.port);

        // Return
        return httpServer;
    }
};

// The module to be exported.
module.exports = {
    version: pkg.version,
    description: pkg.description,
    cli: require('./cli'),
    create: function (config){
        return new NengineServer(config);
    },
    exec: function (cmd, options){
        var help = require('./help'),
            fileConfig = path.join(cwd, 'nengine.json');

        if (fs.existsSync(fileConfig)) {
            fileConfig = require(fileConfig);

            // Clean
            delete fileConfig.root;
        }

        if (options.version) {
            help.version(options.verbose);
        }

        if (options.help) {
            help.help();
            process.exit();
        }

        if (!cmd.length || cmd[0] === 'run') {
            return this.create(mix(fileConfig, options)).run();
        }

        return this;
    }
};<commit_msg>fixed bug<commit_after>/**
 * nengine
 * https://nuintun.github.io/nengine
 *
 * Licensed under the MIT license
 * https://github.com/Nuintun/nengine/blob/master/LICENSE
 */

'use strict';

var fs = require('fs'),
    cwd = process.cwd(),
    dirname = __dirname,
    path = require('path'),
    http = require('http'),
    mix = require('./mix'),
    send = require('./send'),
    log4js = require('log4js'),
    pkg = require('../package.json'),
    nativeRes = require('../native-assets');

/**
 * File send
 * @param requset
 * @param response
 * @param config
 * @returns {SendStream|*|exports}
 */
function fileSend(requset, response, config){
    var that = this,
        steam = send(requset, response, config);

    // Directory
    steam.on('directory', function (fpath){
        switch (config.directory) {
            case 'allow':
                viewFolder.call(that, this, fpath);
                break;
            case 'deny':
                return steam.error(403);
            case 'ignore':
            default:
                return steam.error(404);
        }
    });

    // Error
    steam.on('error', function (err){
        httpError.call(that, this, err);
    });

    // Send
    steam.pipe();

    return steam;
}

/**
 * Error status
 * @param send
 * @param err
 */
function defaultStatus(send, err){
    var message,
        that = this,
        requset = send.requset,
        response = send.response;

    /**
     * Not found
     */
    function notFound(){
        response.statusCode = 404;
        response.setHeader('Content-Type', 'text/html');
        response.end(that.nativeRes.html['404']);
    }

    // Not found
    if (err.status === 404) {
        // Not found favicon.ico use native ico
        if (requset.url === '/favicon.ico') {
            that.logger.warn('Request: /favicon.ico Not Found');

            fs.exists(path.join(send.root, that.favicon), function (exists){
                if (exists) {
                    send.redirect(that.favicon);
                } else {
                    notFound();
                }

            });
        } else {
            message = 'Not Found';

            that.logger.warn('Request: ' + requset.url + ' ' + message);

            notFound();
        }
    } else {
        // Server error
        message = err.message || 'Nengine Server Error';

        that.logger.warn('Request: ' + requset.url + ' ' + message);

        response.statusCode = err.status || 500;
        response.setHeader('Content-Type', 'text/html');
        response.end(that.nativeRes.html['default'](response.statusCode, message));
    }
}

/**
 * Server error
 * @param send
 * @param err
 */
function httpError(send, err){
    var that = this,
        config = that.config,
        status = err.status,
        response = send.response;

    response.statusCode = status;

    if (config.status[status]) {
        fs.exists(path.join(send.root, that.favicon), function (exists){
            if (exists) {
                send.redirect(config.status[status]);
            } else {
                defaultStatus.call(that, send, err);
            }
        });
    } else {
        defaultStatus.call(that, send, err);
    }
}

/**
 * Format to http style
 * @param path
 * @returns {XML|*|string|void}
 */
function httpPath(path){
    return path.replace(/\\/g, '/');
}

/**
 * View folder
 * @param send
 * @param fpath
 */
function viewFolder(send, fpath){
    var that = this,
        dirpath = send.path,
        response = send.response;

    fs.readdir(fpath, function (err, files){
        response.end(that.nativeRes.html['folder'](dirpath, files));
    });
}

/**
 * NengineServer
 * @param options
 * @returns {*}
 * @constructor
 */
function NengineServer(options){
    var logs, logger,
        config = mix({}, options);

    // Format params
    config.root = config.root || cwd;
    config.port = config.port || 80;
    config.directory = config.directory || 'deny';
    config.directory = config.directory.toLowerCase();
    config.status = config.status || {};
    this.nativeRes = nativeRes(config.root);

    // Make log directory
    logs = path.join(config.root, 'logs');

    if (!fs.existsSync(logs)) {
        try {
            fs.mkdirSync(logs);
        } catch (e) {
            return config.log('Please create ".Logs" directory in the root directory');
        }
    }

    // Configure log4js
    log4js.configure({
        appenders: [
            {
                type: 'console'
            },
            {
                type: 'file',
                maxLogSize: 20480,
                filename: path.join(logs, 'nengine.log'),
                category: 'Nengine'
            }
        ]
    });

    // Get logger
    logger = log4js.getLogger('Nengine');
    logger.setLevel('ALL');

    this.logger = logger;
    this.config = config;
    this.pathToRoot = '/' + httpPath(path.relative(config.root, path.dirname(dirname)));
    this.favicon = httpPath(path.join(this.pathToRoot, 'favicon.ico'));

    return this;
}

NengineServer.prototype = {
    run: function (){
        var that = this,
            config = that.config,
            server = config.server;

        // Create server
        var httpServer = http.createServer(function (requset, response){
            if (server) {
                server = server === true ? 'Nengine' + (pkg.version ? '/' + pkg.version : '') : server;
                typeof server === 'string' && response.setHeader('Server', server);
            }

            // Send file
            fileSend.call(that, requset, response, config);
        });

        // Start listening
        httpServer.on('listening', function (){
            that.logger.info('Server runing at port: ' + config.port);
        });

        // Error
        httpServer.on('error', function (err){
            that.logger.error('Server failed to start: ' + err.message);
        });

        // Close
        httpServer.on('close', function (){
            that.logger.info('Server closed');
        });

        // Listen
        httpServer.listen(config.port);

        // Return
        return httpServer;
    }
};

// The module to be exported.
module.exports = {
    version: pkg.version,
    description: pkg.description,
    cli: require('./cli'),
    create: function (config){
        return new NengineServer(config);
    },
    exec: function (cmd, options){
        var help = require('./help'),
            fileConfig = path.join(cwd, 'nengine.json');

        if (fs.existsSync(fileConfig)) {
            fileConfig = require(fileConfig);

            // Clean
            delete fileConfig.root;
        }

        if (options.version) {
            help.version(options.verbose);
        }

        if (options.help) {
            help.help();
            process.exit();
        }

        if (!cmd.length || cmd[0] === 'run') {
            return this.create(mix(fileConfig, options)).run();
        }

        return this;
    }
};<|endoftext|>


<commit_before><commit_msg>networks.get is not part of the API<commit_after><|endoftext|>


<commit_before>var auth = require('../auth');
var config = require('../config');
var express = require('express');
var log = require('../log');
var http = require('http');

/**
 * Expose `app`
 */

var app = module.exports = express.Router();

/**
 * Proxy
 */

app.all('*', function(req, res) {
  var options = {
    host: config.otp.host,
    method: 'GET',
    path: config.otp.path + req.url,
    port: config.otp.port
  };

  var creq = http.request(options, function(cres) {
    cres.setEncoding('utf8');

    var data = '';
    cres.on('data', function(chunk) {
      data += chunk;
    });

    cres.on('end', function() {
      try {
        JSON.parse(data);
      } catch (e) {
        log(e);
        data = {
          id: data,
          options: [],
          routeId: '',
          stops: [],
          error: e
        };
      }

      if (req.query.callback) {
        res.type('text/javascript');
        res.send(200, data);
      } else {
        res.type('application/json');
        res.send(200, data);
      }
    });

  }).on('error', function(e) {
    res.send(500, e);
  });

  creq.end();
});
<commit_msg>Log errors correctly<commit_after>var auth = require('../auth');
var config = require('../config');
var express = require('express');
var log = require('../log');
var http = require('http');

/**
 * Expose `app`
 */

var app = module.exports = express.Router();

/**
 * Proxy
 */

app.all('*', function(req, res) {
  var options = {
    host: config.otp.host,
    method: 'GET',
    path: config.otp.path + req.url,
    port: config.otp.port
  };

  var creq = http.request(options, function(cres) {
    cres.setEncoding('utf8');

    var data = '';
    cres.on('data', function(chunk) {
      data += chunk;
    });

    cres.on('end', function() {
      try {
        JSON.parse(data);
      } catch (e) {
        log.error(e);
        data = {
          id: data,
          options: [],
          routeId: '',
          stops: [],
          error: e
        };
      }

      if (req.query.callback) {
        res.type('text/javascript');
        res.send(200, data);
      } else {
        res.type('application/json');
        res.send(200, data);
      }
    });

  }).on('error', function(e) {
    res.send(500, e);
  });

  creq.end();
});
<|endoftext|>


<commit_before>(function() {

  const fs = require('fs');
  const crypto = require('crypto');
  const path = require('path');
  const request = require('request');
  const tmp = require('tmp');
  const Targz = require('tar.gz');
  const nxtjs = require('nxtjs');
  const config = require('./config');
  const log = require('./log');
  const PackageBuilder = require('./packageBuilder');
  const Nxt = require('./nxt');
  const nxt = new Nxt();

  class Package {

    constructor(manifest) {
      const result = PackageBuilder.validate(manifest);
      if (result instanceof Error) {
        return result;
      }
      this.manifest = manifest;
    };

    static getPackageInfo(packageName) {
      return nxt.request({
        requestType: 'getAlias',
        aliasName: packageName,
      })
      .then(function(result) {
        return nxt.request({
          requestType: 'getTaggedData',
          transaction: result.data.aliasURI,
        });
      })
      .then(this.processPackageInfo, log.error);
    };

    static processPackageInfo(result) {
      return new Promise(function(resolve, reject) {
        if (!result.data || !result.data.transaction) {
          return reject('Package not found');
        }
        let manifest = {};
        let data = result.data.data;
        if (!data) {
          return reject('Transaction has expired');
        }
        if (!result.data.isText) {
          data = Buffer.from(data, 'hex').toString();
        }
        try {
          manifest = JSON.parse(data);
        } catch (e) {
          return reject('Non-JSON data');
        }
        delete result.data.data;
        Object.assign(result.data, manifest);
        const valid = PackageBuilder.validate(result.data);
        if (valid instanceof Error) {
          return reject(valid.message);
        }
        resolve(result.data);
      });
    };

    static install(packageName, targetDir) {
      const parent = this;
      return this.getPackageInfo(packageName)
      .then(this.downloadPackage)
      .then(this.verifyPackage)
      .then(function(tmpFile) {
        return parent.extractPackage(tmpFile, targetDir);
      },
        log.error
      );
    };

    static downloadPackage(packageInfo) {
      const url = packageInfo.resources[packageInfo.version].url;
      const tmpFile = tmp.fileSync();
      const stream = fs.createWriteStream(tmpFile.name);
      return new Promise(function(resolve, reject) {
        request
        .get(url, reject)
        .on('response', function(resp) {
          log.info('GET %s %s', url, resp.statusCode);
          if (resp.statusCode !== 200) {
            return reject('Invalid status code ' + resp.statusCode);
          }
        })
        .on('end', function() {
          resolve({
            manifest: packageInfo,
            tmpFile: tmpFile,
          });
        })
        .on('error', reject)
        .pipe(stream);
      });
    };

    static verifyPackage (args) {
      const hash = crypto.createHash('sha256');
      const stream = fs.createReadStream(args.tmpFile.name);
      const manifest = args.manifest;
      const tmpFile = args.tmpFile;
      return new Promise(function(resolve, reject) {
        stream.on('data', function(data) {
          hash.update(data, 'utf8');
        });
        stream.on('end', function() {
          var checksum = hash.digest('hex');
          if (checksum !== manifest.resources[manifest.version].sha256) {
            return reject('Couldn\'t verify package');
          }
          log.info('Verification passed.');
          log.info('SHA256: %s', checksum);
          resolve(tmpFile);
        });
      });
    };

    static extractPackage(tmpFile, targetDir) {
      return new Promise(function(resolve, reject) {
        const compress = new Targz();
        compress.extract(tmpFile.name, targetDir, function(err) {
          if (err) {
            return reject(err);
          }
          const fullPath = path.resolve(targetDir);
          log.info('Package extracted to %s', fullPath);
          resolve();
        });
      });
    };

    static compressDir(dir, outfile) {
      return new Promise(function(resolve, reject) {
        const compress = new Targz()
        compress.compress(dir, outfile, function(err) {
          if (err) {
            return reject(err);
          }
          log.info('Packaging successful.');
          const hash = crypto.createHash('sha256');
          const stream = fs.createReadStream(outfile);
          stream.on('data', function(data) {
            hash.update(data, 'utf8');
          });
          stream.on('end', function() {
            const checksum = hash.digest('hex');
            log.info('SHA256 checksum: %s', checksum);
            resolve(checksum);
          });
        });
      });
    };

    static extend(packageName, nxtPassphrase) {
      const nxtAcc = nxtjs.secretPhraseToAccountId(nxtPassphrase);
      const publicKey = nxtjs.secretPhraseToPublicKey(nxtPassphrase);
      let signedTx;
      let txId;
      return this.getPackageInfo(packageName)
      .then(function(result) {
        txId = result.transaction;
        return nxt.request({
          requestType: 'extendTaggedData',
          transaction: txId,
          publicKey: publicKey,
          feeNQT: config.get('nxt:dataFee'),
          deadline: config.get('nxt:deadline'),
        });
      })
      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        if (!result.data.unsignedTransactionBytes) {
          throw new Error('Did not receive transaction bytes');
        }
        signedTx = nxt.sign(
          result.data.unsignedTransactionBytes,
          nxtPassphrase
        );
        return nxt.request({
          requestType: 'broadcastTransaction',
          transactionBytes: signedTx,
        });
      })
      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        log.info('Extended transaction %s', txId);
      },
        // Error handler
        log.error
      );
    };

    publish(nxtPassphrase) {
      const manifest = this.manifest;
      const nxtAcc = nxtjs.secretPhraseToAccountId(nxtPassphrase);
      const publicKey = nxtjs.secretPhraseToPublicKey(nxtPassphrase);
      let signedDataTx;
      let signedAliasTx;
      let dataTxId;
      let aliasTxId;
      let prunableAttachmentJSON;

      nxt.request({
        requestType: 'getAlias',
        aliasName: manifest.name,
      })

      .then(function(result) {
        if (result.data.err && result.data.err.errorCode !== 5) {
          throw new Error(result.data.err);
        }
        if (result.data.accountRS && (result.data.accountRS !== nxtAcc)) {
          throw new Error('Alias doesn\'t belong to you');
        }
        return nxt.request({
          requestType: 'uploadTaggedData',
          name: manifest.name,
          description: manifest.description,
          tags: manifest.tags,
          channel: manifest.channel,
          publicKey: publicKey,
          data: Buffer.from(JSON.stringify(manifest)).toString('hex'),
          filename: config.get('filename'),
          type: 'application/json',
          isText: false,
          feeNQT: config.get('nxt:dataFee'),
          deadline: config.get('nxt:deadline'),
        });
      })

      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        if (!result.data.unsignedTransactionBytes) {
          throw new Error('Did not receive transaction bytes');
        }
        prunableAttachmentJSON = JSON.stringify(
          result.data.transactionJSON.attachment
        );
        signedDataTx = nxt.sign(
          result.data.unsignedTransactionBytes,
          nxtPassphrase
        );
        return nxt.request({
          requestType: 'parseTransaction',
          transactionBytes: signedDataTx,
        });
      })

      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        if (!result.data.transaction) {
          throw new Error('Did not receive transaction id');
        }
        return nxt.request({
          requestType: 'setAlias',
          aliasName: manifest.name,
          aliasURI: result.data.transaction,
          publicKey: publicKey,
          feeNQT: config.get('nxt:aliasFee'),
          deadline: config.get('nxt:deadline'),
        });
      })

      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        if (!result.data.unsignedTransactionBytes) {
          throw new Error('Did not receive transaction bytes');
        }
        signedAliasTx = nxt.sign(
          result.data.unsignedTransactionBytes,
          nxtPassphrase
        );
        return nxt.request({
          requestType: 'broadcastTransaction',
          transactionBytes: signedDataTx,
          prunableAttachmentJSON: prunableAttachmentJSON,
        });
      })

      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        dataTxId = result.data.transaction;
        return nxt.request({
          requestType: 'broadcastTransaction',
          transactionBytes: signedAliasTx,
        });
      })

      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        aliasTxId = result.data.transaction;
        log.info('Transaction setAlias: %s', aliasTxId);
        log.info('Transaction uploadTaggedData: %s', dataTxId);
        log.info('Pubished package %s', this.manifest.name);
      },
        // Error handler
        log.error
      );
    };

  };

  module.exports = Package;

})();
<commit_msg>output package name<commit_after>(function() {

  const fs = require('fs');
  const crypto = require('crypto');
  const path = require('path');
  const request = require('request');
  const tmp = require('tmp');
  const Targz = require('tar.gz');
  const nxtjs = require('nxtjs');
  const config = require('./config');
  const log = require('./log');
  const PackageBuilder = require('./packageBuilder');
  const Nxt = require('./nxt');
  const nxt = new Nxt();

  class Package {

    constructor(manifest) {
      const result = PackageBuilder.validate(manifest);
      if (result instanceof Error) {
        return result;
      }
      this.manifest = manifest;
    };

    static getPackageInfo(packageName) {
      return nxt.request({
        requestType: 'getAlias',
        aliasName: packageName,
      })
      .then(function(result) {
        return nxt.request({
          requestType: 'getTaggedData',
          transaction: result.data.aliasURI,
        });
      })
      .then(this.processPackageInfo, log.error);
    };

    static processPackageInfo(result) {
      return new Promise(function(resolve, reject) {
        if (!result.data || !result.data.transaction) {
          return reject('Package not found');
        }
        let manifest = {};
        let data = result.data.data;
        if (!data) {
          return reject('Transaction has expired');
        }
        if (!result.data.isText) {
          data = Buffer.from(data, 'hex').toString();
        }
        try {
          manifest = JSON.parse(data);
        } catch (e) {
          return reject('Non-JSON data');
        }
        delete result.data.data;
        Object.assign(result.data, manifest);
        const valid = PackageBuilder.validate(result.data);
        if (valid instanceof Error) {
          return reject(valid.message);
        }
        resolve(result.data);
      });
    };

    static install(packageName, targetDir) {
      const parent = this;
      return this.getPackageInfo(packageName)
      .then(this.downloadPackage)
      .then(this.verifyPackage)
      .then(function(tmpFile) {
        return parent.extractPackage(tmpFile, targetDir);
      },
        log.error
      );
    };

    static downloadPackage(packageInfo) {
      const url = packageInfo.resources[packageInfo.version].url;
      const tmpFile = tmp.fileSync();
      const stream = fs.createWriteStream(tmpFile.name);
      return new Promise(function(resolve, reject) {
        request
        .get(url, reject)
        .on('response', function(resp) {
          log.info('GET %s %s', url, resp.statusCode);
          if (resp.statusCode !== 200) {
            return reject('Invalid status code ' + resp.statusCode);
          }
        })
        .on('end', function() {
          resolve({
            manifest: packageInfo,
            tmpFile: tmpFile,
          });
        })
        .on('error', reject)
        .pipe(stream);
      });
    };

    static verifyPackage (args) {
      const hash = crypto.createHash('sha256');
      const stream = fs.createReadStream(args.tmpFile.name);
      const manifest = args.manifest;
      const tmpFile = args.tmpFile;
      return new Promise(function(resolve, reject) {
        stream.on('data', function(data) {
          hash.update(data, 'utf8');
        });
        stream.on('end', function() {
          var checksum = hash.digest('hex');
          if (checksum !== manifest.resources[manifest.version].sha256) {
            return reject('Couldn\'t verify package');
          }
          log.info('Verification passed.');
          log.info('SHA256: %s', checksum);
          resolve(tmpFile);
        });
      });
    };

    static extractPackage(tmpFile, targetDir) {
      return new Promise(function(resolve, reject) {
        const compress = new Targz();
        compress.extract(tmpFile.name, targetDir, function(err) {
          if (err) {
            return reject(err);
          }
          const fullPath = path.resolve(targetDir);
          log.info('Package extracted to %s', fullPath);
          resolve();
        });
      });
    };

    static compressDir(dir, outfile) {
      return new Promise(function(resolve, reject) {
        const compress = new Targz()
        compress.compress(dir, outfile, function(err) {
          if (err) {
            return reject(err);
          }
          log.info('Packaging successful.');
          const hash = crypto.createHash('sha256');
          const stream = fs.createReadStream(outfile);
          stream.on('data', function(data) {
            hash.update(data, 'utf8');
          });
          stream.on('end', function() {
            const checksum = hash.digest('hex');
            log.info('SHA256 checksum: %s', checksum);
            resolve(checksum);
          });
        });
      });
    };

    static extend(packageName, nxtPassphrase) {
      const nxtAcc = nxtjs.secretPhraseToAccountId(nxtPassphrase);
      const publicKey = nxtjs.secretPhraseToPublicKey(nxtPassphrase);
      let signedTx;
      let txId;
      return this.getPackageInfo(packageName)
      .then(function(result) {
        txId = result.transaction;
        return nxt.request({
          requestType: 'extendTaggedData',
          transaction: txId,
          publicKey: publicKey,
          feeNQT: config.get('nxt:dataFee'),
          deadline: config.get('nxt:deadline'),
        });
      })
      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        if (!result.data.unsignedTransactionBytes) {
          throw new Error('Did not receive transaction bytes');
        }
        signedTx = nxt.sign(
          result.data.unsignedTransactionBytes,
          nxtPassphrase
        );
        return nxt.request({
          requestType: 'broadcastTransaction',
          transactionBytes: signedTx,
        });
      })
      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        log.info('Extended transaction %s', txId);
      },
        // Error handler
        log.error
      );
    };

    publish(nxtPassphrase) {
      const parent = this;
      const manifest = this.manifest;
      const nxtAcc = nxtjs.secretPhraseToAccountId(nxtPassphrase);
      const publicKey = nxtjs.secretPhraseToPublicKey(nxtPassphrase);
      let signedDataTx;
      let signedAliasTx;
      let dataTxId;
      let aliasTxId;
      let prunableAttachmentJSON;

      nxt.request({
        requestType: 'getAlias',
        aliasName: manifest.name,
      })

      .then(function(result) {
        if (result.data.err && result.data.err.errorCode !== 5) {
          throw new Error(result.data.err);
        }
        if (result.data.accountRS && (result.data.accountRS !== nxtAcc)) {
          throw new Error('Alias doesn\'t belong to you');
        }
        return nxt.request({
          requestType: 'uploadTaggedData',
          name: manifest.name,
          description: manifest.description,
          tags: manifest.tags,
          channel: manifest.channel,
          publicKey: publicKey,
          data: Buffer.from(JSON.stringify(manifest)).toString('hex'),
          filename: config.get('filename'),
          type: 'application/json',
          isText: false,
          feeNQT: config.get('nxt:dataFee'),
          deadline: config.get('nxt:deadline'),
        });
      })

      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        if (!result.data.unsignedTransactionBytes) {
          throw new Error('Did not receive transaction bytes');
        }
        prunableAttachmentJSON = JSON.stringify(
          result.data.transactionJSON.attachment
        );
        signedDataTx = nxt.sign(
          result.data.unsignedTransactionBytes,
          nxtPassphrase
        );
        return nxt.request({
          requestType: 'parseTransaction',
          transactionBytes: signedDataTx,
        });
      })

      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        if (!result.data.transaction) {
          throw new Error('Did not receive transaction id');
        }
        return nxt.request({
          requestType: 'setAlias',
          aliasName: manifest.name,
          aliasURI: result.data.transaction,
          publicKey: publicKey,
          feeNQT: config.get('nxt:aliasFee'),
          deadline: config.get('nxt:deadline'),
        });
      })

      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        if (!result.data.unsignedTransactionBytes) {
          throw new Error('Did not receive transaction bytes');
        }
        signedAliasTx = nxt.sign(
          result.data.unsignedTransactionBytes,
          nxtPassphrase
        );
        return nxt.request({
          requestType: 'broadcastTransaction',
          transactionBytes: signedDataTx,
          prunableAttachmentJSON: prunableAttachmentJSON,
        });
      })

      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        dataTxId = result.data.transaction;
        return nxt.request({
          requestType: 'broadcastTransaction',
          transactionBytes: signedAliasTx,
        });
      })

      .then(function(result) {
        if (result.data.err) {
          throw new Error(result.data.err);
        }
        aliasTxId = result.data.transaction;
        log.info('Transaction setAlias: %s', aliasTxId);
        log.info('Transaction uploadTaggedData: %s', dataTxId);
        log.info('Pubished package %s', parent.manifest.name);
      },
        // Error handler
        log.error
      );
    };

  };

  module.exports = Package;

})();
<|endoftext|>


<commit_before>const fs = require('fs')
const crypto = require('crypto')
const got = require('got')
const baseX = require('base-x')
const querystring = require('querystring')

const E = require('./error')
const selfSign = require('./self_sign')

const PORT = 3000
const ALPHA_BASE = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'

const bsAlpha = baseX(ALPHA_BASE)

// [caHash, token, Buffer.from(hostname)]
function extractHostname (totem) {
  return totem.slice(64 + 16).toString()
}

function pair (totemStr, clientCert, connectionInfoPath, model, numOfCassettes) {
  const totem = Buffer.from(bsAlpha.decode(totemStr))
  const hostname = extractHostname(totem)
  const expectedCaHash = totem.slice(0, 32)
  const token = totem.slice(32, 64 + 16).toString('hex')
  const identifier = totem.slice(64, 64 + 16).toString('hex')
  const hexToken = token.toString('hex')
  const caHexToken = crypto.createHash('sha256').update(hexToken).digest('hex')

  const initialOptions = {
    json: true,
    key: clientCert.key,
    cert: clientCert.cert,
    rejectUnauthorized: false
  }

  return got(`https://${hostname}:${PORT}/ca?token=${caHexToken}&id=${identifier}`, initialOptions)
    .then(r => {
      const ca = r.body.ca
      const caHash = crypto.createHash('sha256').update(ca).digest()

      if (!caHash.equals(expectedCaHash)) throw new E.CaHashError()

      const options = {
        key: clientCert.key,
        cert: clientCert.cert,
        ca
      }

      const query = querystring.stringify({token: hexToken, model, numOfCassettes})
      return got.post(`https://${hostname}:${PORT}/pair?${query}&id=${identifier}`, options)
        .then(() => {
          const connectionInfo = {
            host: hostname,
            ca
          }

          fs.writeFileSync(connectionInfoPath, JSON.stringify(connectionInfo))
        })
    })
   .catch(err => {
     console.log(err)
     throw new Error("Pairing error - Please make sure you have a stable network connection and that you are using the right QR Code")
   })
}

function unpair (connectionInfoPath) {
  fs.unlinkSync(connectionInfoPath)
}

function isPaired (connectionInfoPath) {
  return !!connectionInfo(connectionInfoPath)
}

function connectionInfo (connectionInfoPath) {
  try {
    return JSON.parse(fs.readFileSync(connectionInfoPath))
  } catch (e) {
    return null
  }
}

function init (certPath) {
  return selfSign.generateCertificate()
    .then(cert => {
      fs.writeFileSync(certPath.key, cert.key)
      fs.writeFileSync(certPath.cert, cert.cert)
      return cert
    })
}

function getCert (certPath) {
  try {
    return {
      key: fs.readFileSync(certPath.key),
      cert: fs.readFileSync(certPath.cert)
    }
  } catch (e) {
    return null
  }
}

module.exports = {init, pair, unpair, isPaired, connectionInfo, getCert}
<commit_msg>feat: support older server versions<commit_after>const fs = require('fs')
const crypto = require('crypto')
const got = require('got')
const baseX = require('base-x')
const querystring = require('querystring')

const E = require('./error')
const selfSign = require('./self_sign')

const PORT = 3000
const ALPHA_BASE = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'

const bsAlpha = baseX(ALPHA_BASE)

const offsets = {
  73: {
    caHash: 0,
    token: 32,
    hostname: 64,
  },
  /* Future version, with operator ID */
  89: {
    caHash: 0,
    token: 32,
    identifier: 64,
    hostname: 64+16,
  },
}

// [caHash, token, Buffer.from(hostname)]
function extractHostname (totem, oss) {
  return totem.slice(oss.hostname).toString()
}

function extractCaHash (totem, oss) {
  return totem.slice(oss.caHash, oss.token)
}

function extractToken (totem, oss) {
  return totem.slice(oss.token, oss.hostname).toString('hex')
}

function extractIdentifier (totem, oss) {
  return oss.identifier ?
    totem.slice(oss.identifier, oss.hostname).toString('hex') :
    null
}

function pair (totemStr, clientCert, connectionInfoPath, model, numOfCassettes) {
  const totem = Buffer.from(bsAlpha.decode(totemStr))
  const oss = offsets[totem.length]
  if (!oss) throw new Error('Invalid totem length')

  const hostname = extractHostname(totem, oss)
  const expectedCaHash = extractCaHash(totem, oss)
  const token = extractToken(totem, oss)
  const identifier = extractIdentifier(totem, oss)

  const tokenHash = crypto.createHash('sha256').update(token).digest('hex')
  const initialOptions = {
    json: true,
    key: clientCert.key,
    cert: clientCert.cert,
    rejectUnauthorized: false
  }

  const idQueryParam = identifier ? `&id=${identifier}` : ''
  return got(`https://${hostname}:${PORT}/ca?token=${tokenHash}` + idQueryParam, initialOptions)
    .then(r => {
      const ca = r.body.ca
      const caHash = crypto.createHash('sha256').update(ca).digest()

      if (!caHash.equals(expectedCaHash)) throw new E.CaHashError()

      const options = {
        key: clientCert.key,
        cert: clientCert.cert,
        ca
      }

      const query = querystring.stringify({token, model, numOfCassettes})
      return got.post(`https://${hostname}:${PORT}/pair?${query}` + idQueryParam, options)
        .then(() => {
          const connectionInfo = {
            host: hostname,
            ca
          }

          fs.writeFileSync(connectionInfoPath, JSON.stringify(connectionInfo))
        })
    })
   .catch(err => {
     console.log(err)
     throw new Error("Pairing error - Please make sure you have a stable network connection and that you are using the right QR Code")
   })
}

function unpair (connectionInfoPath) {
  fs.unlinkSync(connectionInfoPath)
}

function isPaired (connectionInfoPath) {
  return !!connectionInfo(connectionInfoPath)
}

function connectionInfo (connectionInfoPath) {
  try {
    return JSON.parse(fs.readFileSync(connectionInfoPath))
  } catch (e) {
    return null
  }
}

function init (certPath) {
  return selfSign.generateCertificate()
    .then(cert => {
      fs.writeFileSync(certPath.key, cert.key)
      fs.writeFileSync(certPath.cert, cert.cert)
      return cert
    })
}

function getCert (certPath) {
  try {
    return {
      key: fs.readFileSync(certPath.key),
      cert: fs.readFileSync(certPath.cert)
    }
  } catch (e) {
    return null
  }
}

module.exports = {init, pair, unpair, isPaired, connectionInfo, getCert}
<|endoftext|>


<commit_before>"use strict";

var fs = require('fs');
var gm = require('gm');
var path = require('path');
var async = require('async');

class Pdf2Img {

    options = {
        type: 'jpg',
        size: 1024,
        density: 600,
        outputdir: null,
        outputname: null,
        page: null,
        quality: 100
    };

    constructor(options) {
        this.options = options
    }

    convert(input, callbackreturn) {
        // Make sure it has correct extension
        if (path.extname(path.basename(input)) != '.pdf') {
            return callbackreturn({
                result: 'error',
                message: 'Unsupported file type.'
            });
        }

        // Check if input file exists
        if (!this.isFileExists(input)) {
            return callbackreturn({
                result: 'error',
                message: 'Input file not found.'
            });
        }

        var stdout = [];
        var output = path.basename(input, path.extname(path.basename(input)));

        // Set output dir
        if (options.outputdir) {
            options.outputdir = options.outputdir + path.sep;
        } else {
            options.outputdir = output + path.sep;
        }

        // Create output dir if it doesn't exists
        if (!isDirExists(options.outputdir)) {
            fs.mkdirSync(options.outputdir);
        }

        // Set output name
        if (options.outputname) {
            options.outputname = options.outputname;
        } else {
            options.outputname = output;
        }

        async.waterfall([
            // Get pages count
            function (callback) {
                gm(input).identify("%p ", function (err, value) {
                    var pageCount = String(value).split(' ');

                    if (!pageCount.length) {
                        callback({
                            result: 'error',
                            message: 'Invalid page number.'
                        }, null);
                    } else {
                        // Convert selected page
                        if (options.page !== null) {
                            if (options.page <= pageCount.length) {
                                callback(null, [options.page]);
                            } else {
                                callback({
                                    result: 'error',
                                    message: 'Invalid page number.'
                                }, null);
                            }
                        } else {
                            callback(null, pageCount);
                        }
                    }

                })

            },

            // Convert pdf file
            function (pages, callback) {
                // Use eachSeries to make sure that conversion has done page by page
                async.eachSeries(pages, function (page, callbackmap) {
                    var inputStream = fs.createReadStream(input);
                    var outputFile = options.outputdir + options.outputname + '_' + page + '.' + options.type;

                    convertPdf2Img(inputStream, outputFile, parseInt(page), function (error, result) {
                        if (error) {
                            return callbackmap(error);
                        }

                        stdout.push(result);
                        return callbackmap(error, result);
                    });
                }, function (e) {
                    if (e) callback(e);

                    return callback(null, {
                        result: 'success',
                        message: stdout
                    });
                });
            }
        ], callbackreturn);
    };

    convertPdf2Img(input, output, page, callback) {
        if (input.path) {
            var filepath = input.path;
        } else {
            return callback({
                result: 'error',
                message: 'Invalid input file path.'
            }, null);
        }

        var filename = filepath + '[' + (page - 1) + ']';
        gm(input, filename)
            .density(options.density, options.density)
            .resize(options.size)
            .quality(options.quality)
            .write(output, function (err) {
                if (err) {
                    return callback({
                        result: 'error',
                        message: 'Can not write output file.'
                    }, null);
                }

                if (!(fs.statSync(output)['size'] / 1000)) {
                    return callback({
                        result: 'error',
                        message: 'Zero sized output image detected.'
                    }, null);
                }

                var results = {
                    page: page,
                    name: path.basename(output),
                    size: fs.statSync(output)['size'] / 1000.0,
                    path: output
                };

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

// Check if directory is exists

    isDirExists(path) {
        try {
            return fs.statSync(path).isDirectory();
        } catch (e) {
            return false;
        }
    }

// Check if file is exists

    isFileExists(path) {
        try {
            return fs.statSync(path).isFile();
        } catch (e) {
            return false;
        }
    }

}

module.exports = Pdf2Img;
<commit_msg>apply OOP encapsulation to support concurrent calls - fix options is not defined<commit_after>"use strict";

var fs = require('fs');
var gm = require('gm');
var path = require('path');
var async = require('async');

class Pdf2Img {

    options = {
        type: 'jpg',
        size: 1024,
        density: 600,
        outputdir: null,
        outputname: null,
        page: null,
        quality: 100
    };

    constructor(options) {
        this.options = options
    }

    convert(input, callbackreturn) {
        // Make sure it has correct extension
        if (path.extname(path.basename(input)) != '.pdf') {
            return callbackreturn({
                result: 'error',
                message: 'Unsupported file type.'
            });
        }

        // Check if input file exists
        if (!this.isFileExists(input)) {
            return callbackreturn({
                result: 'error',
                message: 'Input file not found.'
            });
        }

        var stdout = [];
        var output = path.basename(input, path.extname(path.basename(input)));

        // Set output dir
        if (this.options.outputdir) {
            this.options.outputdir = this.options.outputdir + path.sep;
        } else {
            this.options.outputdir = output + path.sep;
        }

        // Create output dir if it doesn't exists
        if (!isDirExists(this.options.outputdir)) {
            fs.mkdirSync(this.options.outputdir);
        }

        // Set output name
        if (this.options.outputname) {
            this.options.outputname = this.options.outputname;
        } else {
            this.options.outputname = output;
        }

        async.waterfall([
            // Get pages count
            function (callback) {
                gm(input).identify("%p ", function (err, value) {
                    var pageCount = String(value).split(' ');

                    if (!pageCount.length) {
                        callback({
                            result: 'error',
                            message: 'Invalid page number.'
                        }, null);
                    } else {
                        // Convert selected page
                        if (this.options.page !== null) {
                            if (this.options.page <= pageCount.length) {
                                callback(null, [this.options.page]);
                            } else {
                                callback({
                                    result: 'error',
                                    message: 'Invalid page number.'
                                }, null);
                            }
                        } else {
                            callback(null, pageCount);
                        }
                    }

                })

            },

            // Convert pdf file
            function (pages, callback) {
                // Use eachSeries to make sure that conversion has done page by page
                async.eachSeries(pages, function (page, callbackmap) {
                    var inputStream = fs.createReadStream(input);
                    var outputFile = this.options.outputdir + this.options.outputname + '_' + page + '.' + this.options.type;

                    convertPdf2Img(inputStream, outputFile, parseInt(page), function (error, result) {
                        if (error) {
                            return callbackmap(error);
                        }

                        stdout.push(result);
                        return callbackmap(error, result);
                    });
                }, function (e) {
                    if (e) callback(e);

                    return callback(null, {
                        result: 'success',
                        message: stdout
                    });
                });
            }
        ], callbackreturn);
    };

    convertPdf2Img(input, output, page, callback) {
        if (input.path) {
            var filepath = input.path;
        } else {
            return callback({
                result: 'error',
                message: 'Invalid input file path.'
            }, null);
        }

        var filename = filepath + '[' + (page - 1) + ']';
        gm(input, filename)
            .density(this.options.density, this.options.density)
            .resize(this.options.size)
            .quality(this.options.quality)
            .write(output, function (err) {
                if (err) {
                    return callback({
                        result: 'error',
                        message: 'Can not write output file.'
                    }, null);
                }

                if (!(fs.statSync(output)['size'] / 1000)) {
                    return callback({
                        result: 'error',
                        message: 'Zero sized output image detected.'
                    }, null);
                }

                var results = {
                    page: page,
                    name: path.basename(output),
                    size: fs.statSync(output)['size'] / 1000.0,
                    path: output
                };

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

// Check if directory is exists

    isDirExists(path) {
        try {
            return fs.statSync(path).isDirectory();
        } catch (e) {
            return false;
        }
    }

// Check if file is exists

    isFileExists(path) {
        try {
            return fs.statSync(path).isFile();
        } catch (e) {
            return false;
        }
    }

}

module.exports = Pdf2Img;
<|endoftext|>


<commit_before>var stream = require('stream');
var debug = require('debug')('express-dom');

exports.absolute = function absolute(page) {
	page.when('ready', function() {
		return page.run(function() {
			var dloc = document.location;
			var base = dloc.protocol + '//' + dloc.host;
			function absolut(selector, att) {
				var list = document.querySelectorAll(selector);
				var node;
				for (var i=0; i < list.length; i++) {
					node = list.item(i);
					var item = node.attributes.getNamedItem(att);
					if (!item) continue;
					var uloc = new URL(item.nodeValue, base);
					item.nodeValue = uloc.href;
				}
			}
			absolut('a', 'href');
			absolut('img', 'src');
			absolut('video', 'src');
			absolut('object', 'src');
			absolut('link', 'href');
			absolut('script', 'src');
			absolut('include', 'src');
		});
	});
};

exports.mount = function mount(page) {
	page.when('ready', function() {
		return page.run(function() {
			var dloc = document.location;
			var base = dloc.protocol + '//' + dloc.host;
			function mount(selector, att) {
				var list = document.querySelectorAll(selector);
				var node;
				for (var i=0; i < list.length; i++) {
					node = list.item(i);
					var item = node.attributes.getNamedItem(att);
					if (!item) continue;
					var val = item.nodeValue;
					if (!val || val.charAt(0) == '#') continue;
					var uloc = new URL(val, base);
					if (uloc.protocol == dloc.protocol && uloc.host == dloc.host) {
						item.nodeValue = uloc.pathname + uloc.search + uloc.hash;
					}
				}
			}
			mount('a', 'href');
			mount('img', 'src');
			mount('video', 'src');
			mount('object', 'src');
			mount('link', 'href');
			mount('script', 'src');
			mount('include', 'src');
		});
	});
};

exports.html = function html(page, settings, request, response) {
	page.when('idle', function() {
		debug("html plugin idle");
		if (settings.output == null) return page.html().then(function(str) {
			settings.output = str;
		});
	});
};

exports.redirect = function redirect(page, settings, request, response) {
	page.once('navigate', function(uri) {
		response.status(302);
		response.set('Location', uri);
	});
};

exports.referrer = function referrer(page, settings, request) {
	var ref = request.get('Referer') || "";
	settings.scripts.push({
		fn: function(ref) {
			Object.defineProperty(document, "referrer", {
				configurable: true,
				get: function() { return ref; }
			});
		},
		args: [ref]
	});
};

exports.noreq = function noreq(page, settings) {
	settings.allow = "none";
	settings['auto-load-images'] = false;
};

exports.prerender = function prerender(page, settings) {
	settings.scripts.push(function() {
		Object.defineProperty(document, "visibilityState", {
			configurable: true,
			get: function() { return "prerender"; }
		});
	});
};

exports.hide = function hide(page, settings) {
	// also avoid transitions
	settings.style = `
		html {
			display:none !important;
		}
		* {
			-webkit-transition:none !important;
			transition:none !important;
			-webkit-transition-property: none !important;
			transition-property: none !important;
			-webkit-transform: none !important;
			transform: none !important;
			-webkit-animation: none !important;
			animation: none !important;
		}`;
};

exports.nomedia = function nomedia(page, settings) {
	settings['auto-load-images'] = false;
	settings.filters.push(function() {
		if (this.uri.startsWith("data:")) return;
		var path = (new URL(this.uri, document.location)).pathname;
		if (!path) return;
		var basename = path.split("/").pop();
		if (!basename) return;
		var parts = basename.split(".");
		if (parts.length <= 1) return;
		ext = parts.pop().toLowerCase();
		if (ext.endsWith("json")) return;
		if (ext.endsWith("ml")) return;
		if (ext == "js") return;
		this.cancel = true;
	});
};

exports.cookies = function(whitelist) {
	return function cookies(page, settings, request) {
		var cookies = request.cookies || {};
		var keys = Object.keys(cookies);
		debug("settings cookies from request", keys);
		cookies = (whitelist ? keys.filter(function(name) {
			return whitelist[name];
		}) : keys).map(function(name) {
			return name + ' = ' + cookies[name] + "; Path=/";
		});
		if (!cookies.length) return;
		settings.cookies = cookies;
	};
};

exports.png = function png(page, settings, request, response) {
	settings['auto-load-images'] = true;
	settings.style = null;
	var pass = new stream.PassThrough();
	page.when('idle', function() {
		return page.png(pass);
	}).then(function() {
		response.set('Content-Type', 'image/png');
		settings.output = pass;
	});
};

<commit_msg>Better debug cookies plugin<commit_after>var stream = require('stream');
var debug = require('debug')('express-dom');

exports.absolute = function absolute(page) {
	page.when('ready', function() {
		return page.run(function() {
			var dloc = document.location;
			var base = dloc.protocol + '//' + dloc.host;
			function absolut(selector, att) {
				var list = document.querySelectorAll(selector);
				var node;
				for (var i=0; i < list.length; i++) {
					node = list.item(i);
					var item = node.attributes.getNamedItem(att);
					if (!item) continue;
					var uloc = new URL(item.nodeValue, base);
					item.nodeValue = uloc.href;
				}
			}
			absolut('a', 'href');
			absolut('img', 'src');
			absolut('video', 'src');
			absolut('object', 'src');
			absolut('link', 'href');
			absolut('script', 'src');
			absolut('include', 'src');
		});
	});
};

exports.mount = function mount(page) {
	page.when('ready', function() {
		return page.run(function() {
			var dloc = document.location;
			var base = dloc.protocol + '//' + dloc.host;
			function mount(selector, att) {
				var list = document.querySelectorAll(selector);
				var node;
				for (var i=0; i < list.length; i++) {
					node = list.item(i);
					var item = node.attributes.getNamedItem(att);
					if (!item) continue;
					var val = item.nodeValue;
					if (!val || val.charAt(0) == '#') continue;
					var uloc = new URL(val, base);
					if (uloc.protocol == dloc.protocol && uloc.host == dloc.host) {
						item.nodeValue = uloc.pathname + uloc.search + uloc.hash;
					}
				}
			}
			mount('a', 'href');
			mount('img', 'src');
			mount('video', 'src');
			mount('object', 'src');
			mount('link', 'href');
			mount('script', 'src');
			mount('include', 'src');
		});
	});
};

exports.html = function html(page, settings, request, response) {
	page.when('idle', function() {
		debug("html plugin idle");
		if (settings.output == null) return page.html().then(function(str) {
			settings.output = str;
		});
	});
};

exports.redirect = function redirect(page, settings, request, response) {
	page.once('navigate', function(uri) {
		response.status(302);
		response.set('Location', uri);
	});
};

exports.referrer = function referrer(page, settings, request) {
	var ref = request.get('Referer') || "";
	settings.scripts.push({
		fn: function(ref) {
			Object.defineProperty(document, "referrer", {
				configurable: true,
				get: function() { return ref; }
			});
		},
		args: [ref]
	});
};

exports.noreq = function noreq(page, settings) {
	settings.allow = "none";
	settings['auto-load-images'] = false;
};

exports.prerender = function prerender(page, settings) {
	settings.scripts.push(function() {
		Object.defineProperty(document, "visibilityState", {
			configurable: true,
			get: function() { return "prerender"; }
		});
	});
};

exports.hide = function hide(page, settings) {
	// also avoid transitions
	settings.style = `
		html {
			display:none !important;
		}
		* {
			-webkit-transition:none !important;
			transition:none !important;
			-webkit-transition-property: none !important;
			transition-property: none !important;
			-webkit-transform: none !important;
			transform: none !important;
			-webkit-animation: none !important;
			animation: none !important;
		}`;
};

exports.nomedia = function nomedia(page, settings) {
	settings['auto-load-images'] = false;
	settings.filters.push(function() {
		if (this.uri.startsWith("data:")) return;
		var path = (new URL(this.uri, document.location)).pathname;
		if (!path) return;
		var basename = path.split("/").pop();
		if (!basename) return;
		var parts = basename.split(".");
		if (parts.length <= 1) return;
		ext = parts.pop().toLowerCase();
		if (ext.endsWith("json")) return;
		if (ext.endsWith("ml")) return;
		if (ext == "js") return;
		this.cancel = true;
	});
};

exports.cookies = function(whitelist) {
	return function cookies(page, settings, request) {
		var cookies = request.cookies || {};
		var keys = Object.keys(cookies);
		if (whitelist) keys = keys.filter(function(name) {
			return whitelist[name];
		});
		debug("settings cookies", keys);
		cookies = keys.map(function(name) {
			return name + ' = ' + cookies[name] + "; Path=/";
		});
		if (!cookies.length) return;
		settings.cookies = cookies;
	};
};

exports.png = function png(page, settings, request, response) {
	settings['auto-load-images'] = true;
	settings.style = null;
	var pass = new stream.PassThrough();
	page.when('idle', function() {
		return page.png(pass);
	}).then(function() {
		response.set('Content-Type', 'image/png');
		settings.output = pass;
	});
};

<|endoftext|>


<commit_before>exports.categories = {
    'Authentication': {
        bell: {
            url: 'https://github.com/hapijs/bell',
            description: 'Third-party login plugin for hapi with built in Facebook, GitHub, Google, Twitter, Yahoo, Foursquare, and Windows Live support'
        },
        'hapi-auth-anonymous': {
            url: 'https://github.com/codedoctor/hapi-auth-anonymous',
            description: 'Authentication strategy to support anonymous mobile users.'
        },
        'hapi-auth-basic': {
            url: 'https://github.com/hapijs/hapi-auth-basic',
            description: 'An HTTP Basic authentication scheme'
        },
        'hapi-auth-bearer-simple': {
            url: 'https://github.com/Salesflare/hapi-auth-bearer-simple',
            description: 'A simple bearer token based authentication scheme'
        },
        'hapi-auth-bearer-token': {
            url: 'https://github.com/johnbrett/hapi-auth-bearer-token',
            description: 'A bearer token based authentication scheme'
        },
        'hapi-auth-cookie': {
            url: 'https://github.com/hapijs/hapi-auth-cookie',
            description: 'A cookie-based session authentication scheme'
        },
        'hapi-auth-extra': {
            url: 'https://github.com/asafdav/hapi-auth-extra',
            description: 'Additional auth toolbox for HapiJS including ACL support'
        },
        'hapi-auth-hawk': {
            url :'https://github.com/hapijs/hapi-auth-hawk',
            description: 'Hawk authentication plugin'
        },
        'hapi-auth-jwt': {
          url :'https://github.com/ryanfitz/hapi-auth-jwt',
          description: 'JSON Web Token (JWT) authentication plugin'
        },
        'hapi-auth-signature': {
          url :'https://github.com/58bits/hapi-auth-signature',
          description: 'Signature authentication plugin - a wrapper for the Joyent http-signature scheme'
        },
        'hapi-session-mongo': {
            url: 'https://github.com/Mkoopajr/hapi-session-mongo',
            description: 'MongoDB session store and authentication plugin'
        }
    },
    'Documentation': {
        'hapi-swagger': {
            url: 'https://github.com/glennjones/hapi-swagger',
            description: 'A swagger documentation UI generator plugin for hapi'
        },
        'hapi-swagger-models': {
            url: 'https://github.com/codeandfury/node-hapi-swagger-models',
            description: 'Generate Backbone models from a swagger API'
        },
        lout: {
            url: 'https://github.com/hapijs/lout',
            description: 'A browsable documentation generator'
        }
    },
    "Localization/Internationalization": {
        'hapi-l10n-gettext': {
            url: 'https://github.com/maxnachlinger/hapi-l10n-gettext',
            description: 'A localization plug-in for HapiJS'
        },
        hapil18n: {
            url: 'https://github.com/gpierret/hapi18n',
            description: 'i18n for Hapi'
        }
    },
    'Logging/Metrics': {
        blipp: {
            url: 'https://github.com/danielb2/blipp',
            description: 'Displays the routes table at startup'
        },
        good: {
            url: 'https://github.com/hapijs/good',
            description: 'A logging plugin that supports output to console, file and udp/http endpoints'
        },
        'good-reporters': {
            url: 'https://github.com/hapijs/good/tree/master#reporters',
            description: 'List of available good reporter plugins'
        },
        'hapi-statsd': {
            url: 'https://github.com/mac-/hapi-statsd',
            description: 'Sends request round trip metrics to statsd'
        },
        'airbrake-hapi': {
            url: 'https://github.com/Wayfarer247/airbrake-hapi',
            description: 'Sends internal server errors to Airbrake.io'
        }
    },
    'Messaging': {
        'hapi-rabbit': {
            url: 'https://github.com/aduis/hapi-rabbit',
            description: 'A simple rabbitMQ integration plugin for hapi'
        }
    },
    'Security': {
        blankie: {
            url: 'https://github.com/nlf/blankie',
            description: 'A plugin that makes Content-Security-Policy headers easy'
        },
        crumb: {
            url: 'https://github.com/hapijs/crumb',
            description: 'CSRF crumb generation and validation for hapi'
        }
    },
    'Session': {
        yar: {
            url: 'https://github.com/hapijs/yar',
            description: 'A hapi session plugin and cookie jar'
        }
    },
    'Templating': {
        'hapi-dust': {
            url: 'https://github.com/mikefrey/hapi-dust',
            description: 'Compatibility wrapper for dust.js'
        },
        'hapi-json-view': {
            url: 'https://github.com/gergoerdosi/hapi-json-view',
            description: 'JSON view engine for hapi.'
        },
        'hapi-react-views': {
            url: 'https://github.com/jedireza/hapi-react-views',
            description: 'A hapi view engine for React components.'
        },
        'hapi-react': {
            url: 'https://github.com/landau/hapi-react',
            description: 'A port of express-react-views.'
        }
    },
    'Utility': {
        bassmaster: {
            url: 'https://github.com/hapijs/bassmaster',
            description: 'The batch endpoint makes it easy to combine multiple requests into a single one'
        },
        bedwetter: {
            url: 'https://github.com/devinivy/bedwetter',
            description: 'Auto-generated, CRUDdy route handlers for Waterline models in hapi'
        },
        dogwater: {
            url: 'https://github.com/devinivy/dogwater',
            description: 'A hapi plugin integrating Waterline ORM'
        },
        halacious: {
            url: 'https://github.com/bleupen/halacious',
            description: 'A HAL processor for hapi servers'
        },
        'hapi-assets': {
            url: 'https://github.com/poeticninja/hapi-assets',
            description: 'Load assets in views based on node environment.'
        },
        'hapi-cache-buster': {
            url: 'https://github.com/poeticninja/hapi-cache-buster',
            description: 'Browser asset cache buster.'
        },
        'hapi-cloudinary-connector': {
            url: 'https://github.com/gergoerdosi/hapi-cloudinary-connector',
            description: 'Cloudinary connector plugin for hapi.'
        },
        'hapi-level-db': {
            url: 'https://github.com/maxnachlinger/hapi-level-db',
            description: 'HapiJS / LevelDB integration'
        },
        'hapi-magic-filter': {
            url: 'https://github.com/ruiquelhas/hapi-magic-filter',
            description: 'Hapi.js plugin to validate multipart/form-data file contents'
        },
        'hapi-mailer': {
            url: 'https://github.com/gergoerdosi/hapi-mailer',
            description: 'Mailer plugin for hapi.'
        },
        'hapi-mongoose-db-connector' : {
            url: 'https://github.com/codedoctor/hapi-mongoose-db-connector',
            description: 'hapi plugin that connects to mongodb for mongoose apps'
        },
        'hapi-named-routes': {
            url: 'https://github.com/poeticninja/hapi-named-routes',
            description: 'Add named routes to your view templates.'
        },
        'hapi-node-postgres' : {
            url: 'https://github.com/jedireza/hapi-node-postgres',
            description: 'Wrap requests with a pg connection. Uses connection pooling via `node-postgres`.'
        },
        'hapi-router': {
          url: 'https://github.com/bsiddiqui/hapi-router',
          description: 'A plugin to automatically load your routes'
        },
        'hapi-route-directory': {
            url: 'https://github.com/clarkie/hapi-route-directory',
            description: 'A lightweight json route directory'
        },
        'hapi-routes-status' : {
          url: 'https://github.com/codedoctor/hapi-routes-status',
          description: 'Exposes a status route for your node.js/hapi projects.'
        },
        'hapi-sequelize': {
            url: 'https://github.com/codeandfury/node-hapi-sequelize',
            description: 'HAPI Plugin for Sequelize'
        },
        'hapi-sequelized' : {
            url: 'https://github.com/danecando/hapi-sequelized',
            description: 'A plugin for using sequelize with hapi'
        },
        mrhorse: {
            url: 'https://github.com/mark-bradshaw/mrhorse',
            description: 'Plugin for adding pre-handlers and post-handlers to routes'
        },
        poop: {
            url: 'https://github.com/hapijs/poop',
            description: 'Plugin for taking a process dump and cleaning up after an uncaught exception'
        },
        reptile: {
            url: 'https://github.com/hapijs/reptile',
            description: 'A plugin for creating a REPL'
        },
        ridicule: {
            url: 'https://github.com/walmartlabs/ridicule',
            description: 'A plugin for mocking requests'
        },
        scooter: {
            url: 'https://github.com/hapijs/scooter',
            description: 'User-agent information plugin'
        },
        toothache: {
            url: 'https://github.com/smaxwellstewart/toothache',
            description: 'A plugin designed to make it very simple to create RESTful CRUD endpoints for a Hapi server using MongoDB'
        },
        tv: {
            url: 'https://github.com/hapijs/tv',
            description: 'Interactive debug console plugin for hapi servers'
        },
        hapio: {
            url: 'https://github.com/caligone/hapio',
            description: 'A simple bridge plugin between HapiJS and SocketIO'
        },
        'hapi-mongo-models': {
            url: 'https://github.com/jedireza/hapi-mongo-models',
            description: 'MongoDB object models for hapi applications.'
        }
    },
    Boilerplate: {
        'hapi-dash': {
            url: 'https://github.com/smaxwellstewart/hapi-dash',
            description: 'A boilerplate hapi web and API server example, with frontend dashboard'
        },
        'hapi-ninja': {
            url: 'https://github.com/poeticninja/hapi-ninja',
            description: 'Boilerplate Hapi server example. Node.js, Hapi, and Swig.'
        },
        frame: {
            url: 'https://github.com/jedireza/frame',
            description: 'Includes a user system. 100% test coverage. Bring your own front-end.'
        },
        rutha: {
            url: 'https://github.com/molekilla/rutha',
            description: 'frontend stack for Hapi (server, api) and Angular (client)'
        },
        'generator-hapi-style': {
            url: 'https://github.com/jedireza/generator-hapi-style',
            description: 'Yeoman generator for scaffolding hapi apps and plugins.'
        }
    },
    'The extended hapi universe': {
        boom: {
            url: 'https://github.com/hapijs/boom',
            description: 'HTTP-friendly error objects'
        },
        confidence: {
            url: 'https://github.com/hapijs/confidence',
            description: 'A configuration document format, an API, and a foundation for A/B testing'
        },
        faketoe: {
            url: 'https://github.com/hapijs/faketoe',
            description: 'An XML to JSON converter'
        },
        glue: {
            url: 'https://github.com/hapijs/glue',
            description: 'Server composer'
        },
        hoek: {
            url: 'https://github.com/hapijs/hoek',
            description: 'General purpose node utilities'
        },
        joi: {
            url: 'https://github.com/hapijs/joi',
            description: 'Object schema description language and validator for JavaScript objects'
        },
        kilt: {
            url: 'https://github.com/hapijs/kilt',
            description: 'Combine multiple event emitters into a single emitter'
        },
        lab: {
            url: 'https://github.com/hapijs/lab',
            description: 'A simple testing utility with code coverage analysis'
        },
        makemehapi: {
            url: 'https://github.com/hapijs/makemehapi',
            description: 'Self guided workshops to teach you about hapi'
        },
        qs: {
            url: 'https://github.com/hapijs/qs',
            description: 'A querystring parses with support for arrays and objects'
        },
        rejoice: {
            url: 'https://github.com/hapijs/rejoice',
            description: 'hapi.js cli'
        },
        shot: {
            url: 'https://github.com/hapijs/shot',
            description: 'Injects a fake HTTP request/response into your node server logic'
        },
        topo: {
            url: 'https://github.com/hapijs/topo',
            description: 'Topological sorting with grouping support'
        },
        wreck: {
            url: 'https://github.com/hapijs/wreck',
            description: 'HTTP Client utilities'
        }
    }
};
<commit_msg>Added plugin.<commit_after>exports.categories = {
    'Authentication': {
        bell: {
            url: 'https://github.com/hapijs/bell',
            description: 'Third-party login plugin for hapi with built in Facebook, GitHub, Google, Twitter, Yahoo, Foursquare, and Windows Live support'
        },
        'hapi-auth-anonymous': {
            url: 'https://github.com/codedoctor/hapi-auth-anonymous',
            description: 'Authentication strategy to support anonymous mobile users.'
        },
        'hapi-auth-basic': {
            url: 'https://github.com/hapijs/hapi-auth-basic',
            description: 'An HTTP Basic authentication scheme'
        },
        'hapi-auth-bearer-simple': {
            url: 'https://github.com/Salesflare/hapi-auth-bearer-simple',
            description: 'A simple bearer token based authentication scheme'
        },
        'hapi-auth-bearer-token': {
            url: 'https://github.com/johnbrett/hapi-auth-bearer-token',
            description: 'A bearer token based authentication scheme'
        },
        'hapi-auth-cookie': {
            url: 'https://github.com/hapijs/hapi-auth-cookie',
            description: 'A cookie-based session authentication scheme'
        },
        'hapi-auth-extra': {
            url: 'https://github.com/asafdav/hapi-auth-extra',
            description: 'Additional auth toolbox for HapiJS including ACL support'
        },
        'hapi-auth-hawk': {
            url :'https://github.com/hapijs/hapi-auth-hawk',
            description: 'Hawk authentication plugin'
        },
        'hapi-auth-jwt': {
          url :'https://github.com/ryanfitz/hapi-auth-jwt',
          description: 'JSON Web Token (JWT) authentication plugin'
        },
        'hapi-auth-signature': {
          url :'https://github.com/58bits/hapi-auth-signature',
          description: 'Signature authentication plugin - a wrapper for the Joyent http-signature scheme'
        },
        'hapi-session-mongo': {
            url: 'https://github.com/Mkoopajr/hapi-session-mongo',
            description: 'MongoDB session store and authentication plugin'
        }
    },
    'Documentation': {
        'hapi-swagger': {
            url: 'https://github.com/glennjones/hapi-swagger',
            description: 'A swagger documentation UI generator plugin for hapi'
        },
        'hapi-swagger-models': {
            url: 'https://github.com/codeandfury/node-hapi-swagger-models',
            description: 'Generate Backbone models from a swagger API'
        },
        lout: {
            url: 'https://github.com/hapijs/lout',
            description: 'A browsable documentation generator'
        }
    },
    "Localization/Internationalization": {
        'hapi-l10n-gettext': {
            url: 'https://github.com/maxnachlinger/hapi-l10n-gettext',
            description: 'A localization plug-in for HapiJS'
        },
        hapil18n: {
            url: 'https://github.com/gpierret/hapi18n',
            description: 'i18n for Hapi'
        }
    },
    'Logging/Metrics': {
        blipp: {
            url: 'https://github.com/danielb2/blipp',
            description: 'Displays the routes table at startup'
        },
        good: {
            url: 'https://github.com/hapijs/good',
            description: 'A logging plugin that supports output to console, file and udp/http endpoints'
        },
        'good-reporters': {
            url: 'https://github.com/hapijs/good/tree/master#reporters',
            description: 'List of available good reporter plugins'
        },
        'hapi-statsd': {
            url: 'https://github.com/mac-/hapi-statsd',
            description: 'Sends request round trip metrics to statsd'
        },
        'airbrake-hapi': {
            url: 'https://github.com/Wayfarer247/airbrake-hapi',
            description: 'Sends internal server errors to Airbrake.io'
        }
    },
    'Messaging': {
        'hapi-rabbit': {
            url: 'https://github.com/aduis/hapi-rabbit',
            description: 'A simple rabbitMQ integration plugin for hapi'
        }
    },
    'Security': {
        blankie: {
            url: 'https://github.com/nlf/blankie',
            description: 'A plugin that makes Content-Security-Policy headers easy'
        },
        crumb: {
            url: 'https://github.com/hapijs/crumb',
            description: 'CSRF crumb generation and validation for hapi'
        }
    },
    'Session': {
        yar: {
            url: 'https://github.com/hapijs/yar',
            description: 'A hapi session plugin and cookie jar'
        }
    },
    'Templating': {
        'hapi-dust': {
            url: 'https://github.com/mikefrey/hapi-dust',
            description: 'Compatibility wrapper for dust.js'
        },
        'hapi-json-view': {
            url: 'https://github.com/gergoerdosi/hapi-json-view',
            description: 'JSON view engine for hapi.'
        },
        'hapi-react-views': {
            url: 'https://github.com/jedireza/hapi-react-views',
            description: 'A hapi view engine for React components.'
        },
        'hapi-react': {
            url: 'https://github.com/landau/hapi-react',
            description: 'A port of express-react-views.'
        }
    },
    'Utility': {
        bassmaster: {
            url: 'https://github.com/hapijs/bassmaster',
            description: 'The batch endpoint makes it easy to combine multiple requests into a single one'
        },
        bedwetter: {
            url: 'https://github.com/devinivy/bedwetter',
            description: 'Auto-generated, CRUDdy route handlers for Waterline models in hapi'
        },
        dogwater: {
            url: 'https://github.com/devinivy/dogwater',
            description: 'A hapi plugin integrating Waterline ORM'
        },
        halacious: {
            url: 'https://github.com/bleupen/halacious',
            description: 'A HAL processor for hapi servers'
        },
        'hapi-assets': {
            url: 'https://github.com/poeticninja/hapi-assets',
            description: 'Load assets in views based on node environment.'
        },
        'hapi-cache-buster': {
            url: 'https://github.com/poeticninja/hapi-cache-buster',
            description: 'Browser asset cache buster.'
        },
        'hapi-cloudinary-connector': {
            url: 'https://github.com/gergoerdosi/hapi-cloudinary-connector',
            description: 'Cloudinary connector plugin for hapi.'
        },
        'hapi-level-db': {
            url: 'https://github.com/maxnachlinger/hapi-level-db',
            description: 'HapiJS / LevelDB integration'
        },
        'hapi-magic-filter': {
            url: 'https://github.com/ruiquelhas/hapi-magic-filter',
            description: 'Hapi.js plugin to validate multipart/form-data file contents'
        },
        'hapi-mailer': {
            url: 'https://github.com/gergoerdosi/hapi-mailer',
            description: 'Mailer plugin for hapi.'
        },
        'hapi-mongoose-db-connector' : {
            url: 'https://github.com/codedoctor/hapi-mongoose-db-connector',
            description: 'hapi plugin that connects to mongodb for mongoose apps'
        },
        'hapi-named-routes': {
            url: 'https://github.com/poeticninja/hapi-named-routes',
            description: 'Add named routes to your view templates.'
        },
        'hapi-node-postgres' : {
            url: 'https://github.com/jedireza/hapi-node-postgres',
            description: 'Wrap requests with a pg connection. Uses connection pooling via `node-postgres`.'
        },
        'hapi-router': {
          url: 'https://github.com/bsiddiqui/hapi-router',
          description: 'A plugin to automatically load your routes'
        },
        'hapi-route-directory': {
            url: 'https://github.com/clarkie/hapi-route-directory',
            description: 'A lightweight json route directory'
        },
        'hapi-routes-status' : {
          url: 'https://github.com/codedoctor/hapi-routes-status',
          description: 'Exposes a status route for your node.js/hapi projects.'
        },
        'hapi-sequelize': {
            url: 'https://github.com/codeandfury/node-hapi-sequelize',
            description: 'HAPI Plugin for Sequelize'
        },
        'hapi-sequelized' : {
            url: 'https://github.com/danecando/hapi-sequelized',
            description: 'A plugin for using sequelize with hapi'
        },
        'hapi-webpack' : {
            url: 'https://github.com/christophercliff/hapi-webpack',
            description: 'A Hapi plugin for building and serving Webpack bundles.'
        },
        mrhorse: {
            url: 'https://github.com/mark-bradshaw/mrhorse',
            description: 'Plugin for adding pre-handlers and post-handlers to routes'
        },
        poop: {
            url: 'https://github.com/hapijs/poop',
            description: 'Plugin for taking a process dump and cleaning up after an uncaught exception'
        },
        reptile: {
            url: 'https://github.com/hapijs/reptile',
            description: 'A plugin for creating a REPL'
        },
        ridicule: {
            url: 'https://github.com/walmartlabs/ridicule',
            description: 'A plugin for mocking requests'
        },
        scooter: {
            url: 'https://github.com/hapijs/scooter',
            description: 'User-agent information plugin'
        },
        toothache: {
            url: 'https://github.com/smaxwellstewart/toothache',
            description: 'A plugin designed to make it very simple to create RESTful CRUD endpoints for a Hapi server using MongoDB'
        },
        tv: {
            url: 'https://github.com/hapijs/tv',
            description: 'Interactive debug console plugin for hapi servers'
        },
        hapio: {
            url: 'https://github.com/caligone/hapio',
            description: 'A simple bridge plugin between HapiJS and SocketIO'
        },
        'hapi-mongo-models': {
            url: 'https://github.com/jedireza/hapi-mongo-models',
            description: 'MongoDB object models for hapi applications.'
        }
    },
    Boilerplate: {
        'hapi-dash': {
            url: 'https://github.com/smaxwellstewart/hapi-dash',
            description: 'A boilerplate hapi web and API server example, with frontend dashboard'
        },
        'hapi-ninja': {
            url: 'https://github.com/poeticninja/hapi-ninja',
            description: 'Boilerplate Hapi server example. Node.js, Hapi, and Swig.'
        },
        frame: {
            url: 'https://github.com/jedireza/frame',
            description: 'Includes a user system. 100% test coverage. Bring your own front-end.'
        },
        rutha: {
            url: 'https://github.com/molekilla/rutha',
            description: 'frontend stack for Hapi (server, api) and Angular (client)'
        },
        'generator-hapi-style': {
            url: 'https://github.com/jedireza/generator-hapi-style',
            description: 'Yeoman generator for scaffolding hapi apps and plugins.'
        }
    },
    'The extended hapi universe': {
        boom: {
            url: 'https://github.com/hapijs/boom',
            description: 'HTTP-friendly error objects'
        },
        confidence: {
            url: 'https://github.com/hapijs/confidence',
            description: 'A configuration document format, an API, and a foundation for A/B testing'
        },
        faketoe: {
            url: 'https://github.com/hapijs/faketoe',
            description: 'An XML to JSON converter'
        },
        glue: {
            url: 'https://github.com/hapijs/glue',
            description: 'Server composer'
        },
        hoek: {
            url: 'https://github.com/hapijs/hoek',
            description: 'General purpose node utilities'
        },
        joi: {
            url: 'https://github.com/hapijs/joi',
            description: 'Object schema description language and validator for JavaScript objects'
        },
        kilt: {
            url: 'https://github.com/hapijs/kilt',
            description: 'Combine multiple event emitters into a single emitter'
        },
        lab: {
            url: 'https://github.com/hapijs/lab',
            description: 'A simple testing utility with code coverage analysis'
        },
        makemehapi: {
            url: 'https://github.com/hapijs/makemehapi',
            description: 'Self guided workshops to teach you about hapi'
        },
        qs: {
            url: 'https://github.com/hapijs/qs',
            description: 'A querystring parses with support for arrays and objects'
        },
        rejoice: {
            url: 'https://github.com/hapijs/rejoice',
            description: 'hapi.js cli'
        },
        shot: {
            url: 'https://github.com/hapijs/shot',
            description: 'Injects a fake HTTP request/response into your node server logic'
        },
        topo: {
            url: 'https://github.com/hapijs/topo',
            description: 'Topological sorting with grouping support'
        },
        wreck: {
            url: 'https://github.com/hapijs/wreck',
            description: 'HTTP Client utilities'
        }
    }
};
<|endoftext|>


<commit_before>'use strict';

const p = require('path');
const $ = require('./utils');
const flatten = require('flatten');
const Promise = require('bluebird');

const co = Promise.coroutine;
const dirname = p.dirname;
const join = p.join;

const esnext = 'fly-esnext';
const rgx = /^fly-/i;

/**
 * Attempt to dynamically `require()` a file or package
 * @param  {String} name 	The dep-name or filepath to require.
 */
function req(name) {
	try {
		return require(name);
	} catch (e) {
		$.alert(e.message);
	}
}

/**
 * Find a sibling `package.json` file & return its contents.
 * @param  {String} file  The path to `package.json`
 * @return {Array}        The names of all dependencies, flattened
 */
const getDependencies = co(function * (file) {
	if (!file || !(yield $.find(file))) {
		return [];
	}

	// parse file contents as Object
	const pkg = JSON.parse(yield $.read(file));

	// get all possible dependencies
	const deps = ['dependencies', 'devDependencies', 'peerDependencies']
		.filter(key => key in pkg)
		.map(dep => Object.keys(pkg[dep]));

	return flatten(deps);
});

/**
 * Loads all fly-related plugins!
 * @param  {String} flyfile  The full `flyfile.js` path
 * @return {Array}           All loaded plugins.
 */
const load = co(function * (flyfile) {
	// find `package.json` closes to `flyfile`
	const pkg = yield $.find('package.json', dirname(flyfile));

	if (!pkg) {
		$.error('No `package.json` found!');
		return [];
	}

	const modules = join(dirname(pkg), 'node_modules');

	// get ALL deps; filter down to fly-only;
	const deps = yield getDependencies(pkg).filter(dep => rgx.test(dep));
	const hasNext = deps.indexOf(esnext);

	// require fly-esnext; remove from `deps`
	if (hasNext !== -1) {
		deps.splice(hasNext, 1);
		require(esnext);
	}

	// format return;
	return deps.map(str => ({
		name: str,
		func: req(join(modules, str))
	}));
});

module.exports = {
	load: load,
	getDependencies: getDependencies
};
<commit_msg>do not attempt to require fly-esnext while parsing<commit_after>'use strict';

const p = require('path');
const $ = require('./utils');
const flatten = require('flatten');
const Promise = require('bluebird');

const co = Promise.coroutine;
const dirname = p.dirname;
const join = p.join;

const rgx = /^fly-/i;

/**
 * Attempt to dynamically `require()` a file or package
 * @param  {String} name 	The dep-name or filepath to require.
 */
function req(name) {
	try {
		return require(name);
	} catch (e) {
		$.alert(e.message);
	}
}

/**
 * Find a sibling `package.json` file & return its contents.
 * @param  {String} file  The path to `package.json`
 * @return {Array}        The names of all dependencies, flattened
 */
const getDependencies = co(function * (file) {
	if (!file || !(yield $.find(file))) {
		return [];
	}

	// parse file contents as Object
	const pkg = JSON.parse(yield $.read(file));

	// get all possible dependencies
	const deps = ['dependencies', 'devDependencies', 'peerDependencies']
		.filter(key => key in pkg)
		.map(dep => Object.keys(pkg[dep]));

	return flatten(deps);
});

/**
 * Loads all fly-related plugins!
 * @param  {String} flyfile  The full `flyfile.js` path
 * @return {Array}           All loaded plugins.
 */
const load = co(function * (flyfile) {
	// find `package.json` closes to `flyfile`
	const pkg = yield $.find('package.json', dirname(flyfile));

	if (!pkg) {
		$.error('No `package.json` found!');
		return [];
	}

	const modules = join(dirname(pkg), 'node_modules');

	// get ALL deps; filter down to fly-only;
	const deps = yield getDependencies(pkg).filter(dep => rgx.test(dep));
	const hasNext = deps.indexOf('fly-esnext');

	// if 'fly-esnext' remove from `deps`
	if (hasNext !== -1) {
		deps.splice(hasNext, 1);
	}

	// format return;
	return deps.map(str => ({
		name: str,
		func: req(join(modules, str))
	}));
});

module.exports = {
	load: load,
	getDependencies: getDependencies
};
<|endoftext|>


<commit_before>"use strict";

var childprocess = require('child_process');
var fs = require('fs');
var path = require('path');

var opts = {
  output: null,
  outputdir: null,
  pagerange: null
};

var Ppt2Pdf = function() {};

Ppt2Pdf.prototype.convert = function(input, options, callback) {
  switch (path.extname(path.basename(input))) {
    case '.odp':
    case '.ppt':
    case '.pptx': if (!isFileExists(input)) {
      return callback({
        result: 'error',
        message: 'Input file not found.'
      });
    } break;
    default: return callback({
      result: 'error',
      message: 'Unsupported file type.'
    }); break;
  }

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

  opts.output = options.output || opts.output;
  opts.outputdir = options.outputdir || opts.outputdir;
  opts.pagerange = options.pagerange || opts.pagerange;

  var stdout = [];
  var stderr = [];

  var args = [
    '-fpdf',
    '--stdout'
  ];

  if (opts.pagerange) {
    args.push('-e', 'PageRange=' + opts.pagerange);
  }

  if (!opts.output) {
    var out = path.basename(input, path.extname(path.basename(input)));
    opts.output = out + '.pdf'
  }

  if (opts.outputdir) {
    if (!isDirExists(opts.outputdir)) {
      fs.mkdirSync(opts.outputdir);
    }

    opts.outputdir = opts.outputdir + '/';
  } else {
    opts.outputdir = '';
  }

  args.push(input);

  var child = childprocess.spawn('unoconv', args);

  child.stdout.on('data', function(data) {
    stdout.push(data);
  });

  child.stderr.on('data', function(data) {
    stderr.push(data);
  });

  child.on('exit', function() {
    if (stderr.length) {
      return callback({
        result: 'error',
        message: Buffer.concat(stderr).toString().trim()
      });
    }

    var buffer = Buffer.concat(stdout);

    fs.writeFile(opts.outputdir + opts.output, buffer);

    callback(null, {
      result: 'success',
      message: path.basename(opts.output),
    });
  });
};

var isDirExists = function(path) {
  try {
    return fs.statSync(path).isDirectory();
  } catch (e) {
    return false;
  }
}

var isFileExists = function(path) {
  try {
    return fs.statSync(path).isFile();
  } catch (e) {
    return false;
  }
}

module.exports = new Ppt2Pdf;
<commit_msg>Add extension in output file.<commit_after>"use strict";

var childprocess = require('child_process');
var fs = require('fs');
var path = require('path');

var opts = {
  output: null,
  outputdir: null,
  pagerange: null
};

var Ppt2Pdf = function() {};

Ppt2Pdf.prototype.convert = function(input, options, callback) {
  switch (path.extname(path.basename(input))) {
    case '.odp':
    case '.ppt':
    case '.pptx': if (!isFileExists(input)) {
      return callback({
        result: 'error',
        message: 'Input file not found.'
      });
    } break;
    default: return callback({
      result: 'error',
      message: 'Unsupported file type.'
    }); break;
  }

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

  opts.output = options.output || opts.output;
  opts.outputdir = options.outputdir || opts.outputdir;
  opts.pagerange = options.pagerange || opts.pagerange;

  var stdout = [];
  var stderr = [];

  var args = [
    '-fpdf',
    '--stdout'
  ];

  if (opts.pagerange) {
    args.push('-e', 'PageRange=' + opts.pagerange);
  }

  if (!opts.output) {
    var out = path.basename(input, path.extname(path.basename(input)));
    opts.output = out + '.pdf'
  } else {
    opts.output = opts.output + '.pdf';
  }

  if (opts.outputdir) {
    if (!isDirExists(opts.outputdir)) {
      fs.mkdirSync(opts.outputdir);
    }

    opts.outputdir = opts.outputdir + '/';
  } else {
    opts.outputdir = '';
  }

  args.push(input);

  var child = childprocess.spawn('unoconv', args);

  child.stdout.on('data', function(data) {
    stdout.push(data);
  });

  child.stderr.on('data', function(data) {
    stderr.push(data);
  });

  child.on('exit', function() {
    if (stderr.length) {
      return callback({
        result: 'error',
        message: Buffer.concat(stderr).toString().trim()
      });
    }

    var buffer = Buffer.concat(stdout);

    fs.writeFile(opts.outputdir + opts.output, buffer);

    callback(null, {
      result: 'success',
      message: path.basename(opts.output),
    });
  });
};

var isDirExists = function(path) {
  try {
    return fs.statSync(path).isDirectory();
  } catch (e) {
    return false;
  }
}

var isFileExists = function(path) {
  try {
    return fs.statSync(path).isFile();
  } catch (e) {
    return false;
  }
}

module.exports = new Ppt2Pdf;
<|endoftext|>