<commit_before>var express     = require('express')
  , Promise     = require('bluebird')
  , config      = require('config')
  , winston     = require('winston')
  , _           = require('lodash')
  , fs          = require('fs-extra')
  , safe_parse  = require("safe-json-parse/tuple")
  , Headers     = require('./headers')
  , Cloud       = require('./storage/cloud')
  , Cache       = require('./storage/cache')
  , Local       = require('./storage/local')
  //
  , app         = express()
  ;

app.post('/write/:etag', function(req, res){
  var etag      = req.params.etag
    , metadata  = safe_parse(req.get('__metadata'))[1] || {}
    , headers   = new Headers(metadata.headers)
    , cache     = new Cache(etag)

  // console.log('write-req', etag, headers);
  // note [akamel] ensure that the __write lock is set sync from here, otherwise a read might come in before it is set.
  cache
    .write(headers, req, { metadata : metadata }) ])
    .then(() => {
      res.send({ message : 'OK' });
    })
    .catch((err) => {
      winston.error('error writing', err)
      res.status(500).send({ message : err.message });
    });
});

app.get('/metadata/:etag', function(req, res, next){
  var etag  = req.params.etag
    , cache = new Cache(etag)
    ;

  Promise
    .all([ cache.metadata() ])
    .spread((metadata) => {
      // console.log('cache-hit', etag);
      res.send(metadata);
    })
    .catch((err) => {
      Local.debug();
      winston.error('cache-miss /metadata', etag, err);
      res.status(404).send({ message : 'not found' });
    });
});

app.get('/read/:etag', function(req, res, next){
  var etag  = req.params.etag
    , cache = new Cache(etag)
    ;

  Promise
    .all([ cache.read(), cache.metadata() ])
    .spread((stream, metadata) => {
      res.set(metadata.headers);
      res.set('Age', metadata.age);
      res.status(metadata['status-code']);

      stream.pipe(res);
    })
    .catch((err) => {
      winston.error('cache-miss /read', etag, err);
      res.status(404).send({ message : 'not found' });
    });
});

function listen(options, cb) {
  return Promise.fromCallback((cb) => {
    app.listen(config.get('log').port, cb);

    fs.ensureDir('.cache', () => {
      Cache.scan('.cache');
    });
  });
  // var opts = {};

  // if (process.env.NODE_ENV === 'production') {
  //   _.defaults(opts, {
  //       ca    : [
  //           fs.readFileSync('../.key/cert/signed/AddTrustExternalCARoot.crt')
  //         , fs.readFileSync('../.key/cert/signed/COMODORSAAddTrustCA.crt')
  //         , fs.readFileSync('../.key/cert/signed/COMODORSADomainValidationSecureServerCA.crt')
  //       ]
  //     , key   : fs.readFileSync('../.key/cert/server.key')
  //     , cert  : fs.readFileSync('../.key/cert/signed/taskmill_io.crt')
  //   });
  // }

  // https.createServer(opts, app).listen(config.get('http.port'), () => {
  //    console.log('Started!');
  // });
}

module.exports = {
    listen : listen
};<commit_msg>fix typo<commit_after>var express     = require('express')
  , Promise     = require('bluebird')
  , config      = require('config')
  , winston     = require('winston')
  , _           = require('lodash')
  , fs          = require('fs-extra')
  , safe_parse  = require("safe-json-parse/tuple")
  , Headers     = require('./headers')
  , Cloud       = require('./storage/cloud')
  , Cache       = require('./storage/cache')
  , Local       = require('./storage/local')
  //
  , app         = express()
  ;

app.post('/write/:etag', function(req, res){
  var etag      = req.params.etag
    , metadata  = safe_parse(req.get('__metadata'))[1] || {}
    , headers   = new Headers(metadata.headers)
    , cache     = new Cache(etag)

  // console.log('write-req', etag, headers);
  // note [akamel] ensure that the __write lock is set sync from here, otherwise a read might come in before it is set.
  cache
    .write(headers, req, { metadata : metadata })
    .then(() => {
      res.send({ message : 'OK' });
    })
    .catch((err) => {
      winston.error('error writing', err)
      res.status(500).send({ message : err.message });
    });
});

app.get('/metadata/:etag', function(req, res, next){
  var etag  = req.params.etag
    , cache = new Cache(etag)
    ;

  Promise
    .all([ cache.metadata() ])
    .spread((metadata) => {
      // console.log('cache-hit', etag);
      res.send(metadata);
    })
    .catch((err) => {
      Local.debug();
      winston.error('cache-miss /metadata', etag, err);
      res.status(404).send({ message : 'not found' });
    });
});

app.get('/read/:etag', function(req, res, next){
  var etag  = req.params.etag
    , cache = new Cache(etag)
    ;

  Promise
    .all([ cache.read(), cache.metadata() ])
    .spread((stream, metadata) => {
      res.set(metadata.headers);
      res.set('Age', metadata.age);
      res.status(metadata['status-code']);

      stream.pipe(res);
    })
    .catch((err) => {
      winston.error('cache-miss /read', etag, err);
      res.status(404).send({ message : 'not found' });
    });
});

function listen(options, cb) {
  return Promise.fromCallback((cb) => {
    app.listen(config.get('log').port, cb);

    fs.ensureDir('.cache', () => {
      Cache.scan('.cache');
    });
  });
  // var opts = {};

  // if (process.env.NODE_ENV === 'production') {
  //   _.defaults(opts, {
  //       ca    : [
  //           fs.readFileSync('../.key/cert/signed/AddTrustExternalCARoot.crt')
  //         , fs.readFileSync('../.key/cert/signed/COMODORSAAddTrustCA.crt')
  //         , fs.readFileSync('../.key/cert/signed/COMODORSADomainValidationSecureServerCA.crt')
  //       ]
  //     , key   : fs.readFileSync('../.key/cert/server.key')
  //     , cert  : fs.readFileSync('../.key/cert/signed/taskmill_io.crt')
  //   });
  // }

  // https.createServer(opts, app).listen(config.get('http.port'), () => {
  //    console.log('Started!');
  // });
}

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


<commit_before>var util = require('util'),
    fs   = require('fs'),
    path = require('path'),
    mocha = require('mocha'),
    _ = require('lodash'),
    handlebars = require('handlebars'),
    less = require('less'),
    moment = require('moment'),
    uuid = require('node-uuid');

var Base = mocha.reporters.Base,
    utils = mocha.utils;

var splitChar = process.platform === "win32" ? '\\' : '/',
    reportsDir = path.join(process.cwd(), 'reports'),
    reportJsonFile = path.join(reportsDir, 'reports.json'),
    reportHtmlFile = path.join(reportsDir, 'mochawesome.html');
    reportCssFile = path.join(reportsDir, 'mochawesome.css'),
    templatesDir = path.join(__dirname, '..', 'templates'),
    templates = {};

module.exports = Mochawesome;

createStylesheet();
loadTemplates();
initHandlebarsHelpers();

/**
 * Initialize a new reporter.
 *
 * @param {Runner} runner
 * @api public
 */

function Mochawesome (runner) {
  var self = this;
  Base.call(this, runner);

  var allSuites = {},
      allTests = [],
      allFailures = [],
      allPasses = [],
      endCalled = false;

  runner.on('test end', function (test) {
    allTests.push(test);
  });

  runner.on('pass', function (test) {
    allPasses.push(test);
  });

  runner.on('fail', function (test){
    allFailures.push(test);
  });

  runner.on('end', function () {
    if (!endCalled) {
      endCalled = true; // end gets called more than once for some reason so this ensures we only do this once

      allSuites = self.runner.suite;
      removeAllPropsFromObjExcept(allSuites, ['suites', 'root']);
      traverseSuites(allSuites);

      var obj = {
        reportTitle: process.cwd().split(splitChar).pop(),
        stats: self.stats,
        suites: allSuites.suites,
        tests: allTests.map(cleanTest),
        passes: allPasses.map(cleanTest),
        failures: allFailures.map(cleanTest)
      };

      var passPercentage = Math.round((allPasses.length / allTests.length)*1000)/10;
      var percentClass;
      if (passPercentage < 50) {
        percentClass = 'danger';
      } else if (passPercentage >= 50 && passPercentage < 80) {
        percentClass = 'warning';
      } else {
        percentClass = 'success';
      }
      obj.stats.passPercent = passPercentage;
      obj.stats.percentClass = percentClass;

      saveToFile('json', obj);
      saveToFile('html', obj);
    }
  });
}

/**
 * HELPER FUNCTIONS
 */

/**
 * Do a breadth-first search to find
 * and format all nested 'suite' objects.
 *
 * @param {Object} suite
 * @api private
 */

function traverseSuites (suite) {
  var queue = [],
      next = suite;
  while (next) {
    if (next.suites.length) {
      _.each(next.suites, function(suite, i) {
        cleanSuite(suite);
        queue.push(suite);
      });
    }
    next = queue.shift();
  }
}

/**
 * Modify the suite object to add properties needed to render
 * the template and remove properties we do not need.
 *
 * @param {Object} suite
 * @api private
 */

function cleanSuite (suite) {
  var cleanTests = _.map(suite.tests, cleanTest);
  var passingTests = _.where(cleanTests, {state: 'passed'});
  var failingTests = _.where(cleanTests, {state: 'failed'});

  suite.uuid = uuid.v4();
  suite.tests = cleanTests;
  suite.fullFile = suite.file;
  suite.file = suite.file.replace(process.cwd(), '');
  suite.passes = passingTests;
  suite.failures = failingTests;
  suite.totalTests = suite.tests.length;
  suite.totalPasses = passingTests.length;
  suite.totalFailures = failingTests.length;

  removeAllPropsFromObjExcept(suite, [
    'title',
    'tests',
    'suites',
    'file',
    'fullFile',
    'passes',
    'failures',
    'totalTests',
    'totalPasses',
    'totalFailures'
  ]);
}

/**
 * Return a plain-object representation of `test`
 * free of cyclic properties etc.
 *
 * @param {Object} test
 * @return {Object}
 * @api private
 */

function cleanTest (test) {
  return {
    title: test.title,
    fullTitle: test.fullTitle(),
    duration: test.duration,
    state: test.state,
    pass: test.state === 'passed',
    fail: test.state === 'failed',
    code: utils.clean(test.fn.toString()),
    err: test.err
  }
}

/**
 * Remove all properties from an object except
 * those that are in the propsToKeep array.
 *
 * @param {Object} obj
 * @param {Array} propsToKeep
 * @api private
 */

function removeAllPropsFromObjExcept(obj, propsToKeep) {
  _.forOwn(obj, function(val, prop) {
    if (propsToKeep.indexOf(prop) === -1) {
      delete obj[prop];
    }
  });
}

/**
 * Compile LESS stylesheet into CSS and save to file.
 *
 * @api private
 */

function createStylesheet () {
  var stylesDir = path.join(templatesDir, 'styles');
  var bsPath = path.join(__dirname, '..', 'node_modules', 'bootstrap', 'less');
  var stylesheet = fs.readFileSync(path.join(stylesDir, 'mochawesome.less'), {encoding: 'utf8'});

  var parser = new(less.Parser)({
    paths: [stylesDir, bsPath], // Specify search paths for @import directives
  });

  parser.parse(stylesheet, function(e, tree) {
    if (e) throw e;
    var outCss = tree.toCSS({
      compress: true
    });
    saveToFile('css', outCss);
  });
}

/**
 * Load and compile template files from templates directory.
 * Register any partials found in the directory.
 *
 * @api private
 */

function loadTemplates() {
  fs.readdir(templatesDir, function (err, files) {
    if (err) throw err;
    files.forEach(function (file) {
      var filePath = path.join(templatesDir, file);
      fs.stat(filePath, function (err, stats) {
        if (err) throw err;
        if (!stats.isDirectory()) {
          var fileName = file.replace('.mu','');
          fs.readFile(filePath, {encoding:'utf8'}, function (err, data) {
            if (err) throw err;
            if (file.indexOf('_') === 0) {
              handlebars.registerPartial(fileName, data);
            } else {
              templates[fileName] = handlebars.compile(data);
            }
          });
        }
      });
    });
  });
}

function loadTemplatesSync() {
  var files = fs.readdirSync(templatesDir);
  files.forEach(function (file) {
    var filePath = path.join(templatesDir, file);
    var fileStats = fs.statSync(filePath);
    var fileName = file.replace('.mu','');
    if (!fileStats.isDirectory()) {
      var file = fs.readFileSync(filePath, {encoding:'utf8'});
      if (file.indexOf('_') === 0) {
        handlebars.registerPartial(fileName, file);
      } else {
        templates[fileName] = handlebars.compile(file);
      }
    }
  });
}

/**
 * Save data out to files
 *
 * @param {String} json, html
 * @param {Object}
 * @api private
 */

function saveToFile (filetype, inData) {
  var outData, outFile, writeFile;
  var outMsg = '';
  switch (filetype) {
  case 'json':
    outData = JSON.stringify(inData, null, 2);
    outFile = reportJsonFile;
    break;
  case 'html':
    outData = templates.mochawesome(inData);
    outFile = reportHtmlFile;
    outMsg = "\nopen " + outFile.replace(process.cwd(),'').replace('/', '') + "\n";
    break;
  case 'css':
    outData = inData;
    outFile = reportCssFile;
    break;
  }

  try {
    if (!fs.existsSync(reportsDir)) {
      fs.mkdirSync(reportsDir);
    };
    writeFile = fs.openSync(outFile, 'w');
    fs.writeSync(writeFile, outData);
    fs.close(writeFile);
    util.print("Saved " + outFile + "\n" + outMsg);

  } catch (err) {
    util.print("\nError: Unable to save " + outFile + "\n" + err);
  }
}

/**
 * Register Handlebars helpers used in template
 *
 * @api private
 */

function initHandlebarsHelpers () {
  handlebars.registerHelper('toSeconds', function (context) {
    return context/1000;
  });

  handlebars.registerHelper('dateFormat', function(context, format) {
    if (format === "fromNow") {
      return moment(context).fromNow();
    } else {
      return moment(context).format(format);
    }
  });
}<commit_msg>ignore fiveby wrapper test<commit_after>var util = require('util'),
    fs   = require('fs'),
    path = require('path'),
    mocha = require('mocha'),
    _ = require('lodash'),
    handlebars = require('handlebars'),
    less = require('less'),
    moment = require('moment'),
    uuid = require('node-uuid');

var Base = mocha.reporters.Base,
    utils = mocha.utils;

var splitChar = process.platform === "win32" ? '\\' : '/',
    reportsDir = path.join(process.cwd(), 'reports'),
    reportJsonFile = path.join(reportsDir, 'reports.json'),
    reportHtmlFile = path.join(reportsDir, 'mochawesome.html');
    reportCssFile = path.join(reportsDir, 'mochawesome.css'),
    templatesDir = path.join(__dirname, '..', 'templates'),
    templates = {};

module.exports = Mochawesome;

createStylesheet();
loadTemplates();
initHandlebarsHelpers();

/**
 * Initialize a new reporter.
 *
 * @param {Runner} runner
 * @api public
 */

function Mochawesome (runner) {
  var self = this;
  Base.call(this, runner);

  var allSuites = {},
      allTests = [],
      allFailures = [],
      allPasses = [],
      endCalled = false;

  runner.on('test end', function (test) {
    allTests.push(test);
  });

  runner.on('pass', function (test) {
    allPasses.push(test);
  });

  runner.on('fail', function (test){
    allFailures.push(test);
  });

  runner.on('end', function () {
    if (!endCalled) {
      endCalled = true; // end gets called more than once for some reason so this ensures we only do this once

      allSuites = self.runner.suite;
      removeAllPropsFromObjExcept(allSuites, ['suites', 'root']);
      traverseSuites(allSuites);

      // adjust for fiveby wrapper test
      self.stats.tests -= 1;
      self.stats.passes -= 1;

      var obj = {
        reportTitle: process.cwd().split(splitChar).pop(),
        stats: self.stats,
        suites: allSuites.suites,
        tests: allTests.map(cleanTest).filter(Boolean),
        passes: allPasses.map(cleanTest).filter(Boolean),
        failures: allFailures.map(cleanTest).filter(Boolean)
      };

      var passPercentage = Math.round((obj.stats.passes / obj.stats.tests)*1000)/10;
      var percentClass;
      if (passPercentage <= 50) {
        percentClass = 'danger';
      } else if (passPercentage > 50 && passPercentage < 80) {
        percentClass = 'warning';
      } else {
        percentClass = 'success';
      }
      obj.stats.passPercent = passPercentage;
      obj.stats.percentClass = percentClass;

      saveToFile('json', obj);
      saveToFile('html', obj);
    }
  });
}

/**
 * HELPER FUNCTIONS
 */

/**
 * Do a breadth-first search to find
 * and format all nested 'suite' objects.
 *
 * @param {Object} suite
 * @api private
 */

function traverseSuites (suite) {
  var queue = [],
      next = suite;
  while (next) {
    if (next.suites.length) {
      _.each(next.suites, function(suite, i) {
        cleanSuite(suite);
        queue.push(suite);
      });
    }
    next = queue.shift();
  }
}

/**
 * Modify the suite object to add properties needed to render
 * the template and remove properties we do not need.
 *
 * @param {Object} suite
 * @api private
 */

function cleanSuite (suite) {
  var cleanTests = _.map(suite.tests, cleanTest).filter(Boolean);
  var passingTests = _.where(cleanTests, {state: 'passed'});
  var failingTests = _.where(cleanTests, {state: 'failed'});

  suite.uuid = uuid.v4();
  suite.tests = cleanTests;
  suite.fullFile = suite.file;
  suite.file = suite.file.replace(process.cwd(), '');
  suite.passes = passingTests;
  suite.failures = failingTests;
  suite.totalTests = suite.tests.length;
  suite.totalPasses = passingTests.length;
  suite.totalFailures = failingTests.length;

  removeAllPropsFromObjExcept(suite, [
    'title',
    'tests',
    'suites',
    'file',
    'fullFile',
    'passes',
    'failures',
    'totalTests',
    'totalPasses',
    'totalFailures'
  ]);
}

/**
 * Return a plain-object representation of `test`
 * free of cyclic properties etc.
 *
 * @param {Object} test
 * @return {Object}
 * @api private
 */

function cleanTest (test) {
  if (test.title !== 'prepping tests...') {
    return {
      title: test.title,
      fullTitle: test.fullTitle(),
      duration: test.duration,
      state: test.state,
      pass: test.state === 'passed',
      fail: test.state === 'failed',
      code: utils.clean(test.fn.toString()),
      err: test.err
    }
  }
}

/**
 * Remove all properties from an object except
 * those that are in the propsToKeep array.
 *
 * @param {Object} obj
 * @param {Array} propsToKeep
 * @api private
 */

function removeAllPropsFromObjExcept(obj, propsToKeep) {
  _.forOwn(obj, function(val, prop) {
    if (propsToKeep.indexOf(prop) === -1) {
      delete obj[prop];
    }
  });
}

/**
 * Compile LESS stylesheet into CSS and save to file.
 *
 * @api private
 */

function createStylesheet () {
  var stylesDir = path.join(templatesDir, 'styles');
  var bsPath = path.join(__dirname, '..', 'node_modules', 'bootstrap', 'less');
  var stylesheet = fs.readFileSync(path.join(stylesDir, 'mochawesome.less'), {encoding: 'utf8'});

  var parser = new(less.Parser)({
    paths: [stylesDir, bsPath], // Specify search paths for @import directives
  });

  parser.parse(stylesheet, function(e, tree) {
    if (e) throw e;
    var outCss = tree.toCSS({
      compress: true
    });
    saveToFile('css', outCss);
  });
}

/**
 * Load and compile template files from templates directory.
 * Register any partials found in the directory.
 *
 * @api private
 */

function loadTemplates() {
  fs.readdir(templatesDir, function (err, files) {
    if (err) throw err;
    files.forEach(function (file) {
      var filePath = path.join(templatesDir, file);
      fs.stat(filePath, function (err, stats) {
        if (err) throw err;
        if (!stats.isDirectory()) {
          var fileName = file.replace('.mu','');
          fs.readFile(filePath, {encoding:'utf8'}, function (err, data) {
            if (err) throw err;
            if (file.indexOf('_') === 0) {
              handlebars.registerPartial(fileName, data);
            } else {
              templates[fileName] = handlebars.compile(data);
            }
          });
        }
      });
    });
  });
}

function loadTemplatesSync() {
  var files = fs.readdirSync(templatesDir);
  files.forEach(function (file) {
    var filePath = path.join(templatesDir, file);
    var fileStats = fs.statSync(filePath);
    var fileName = file.replace('.mu','');
    if (!fileStats.isDirectory()) {
      var file = fs.readFileSync(filePath, {encoding:'utf8'});
      if (file.indexOf('_') === 0) {
        handlebars.registerPartial(fileName, file);
      } else {
        templates[fileName] = handlebars.compile(file);
      }
    }
  });
}

/**
 * Save data out to files
 *
 * @param {String} json, html
 * @param {Object}
 * @api private
 */

function saveToFile (filetype, inData) {
  var outData, outFile, writeFile;
  var outMsg = '';
  switch (filetype) {
  case 'json':
    outData = JSON.stringify(inData, null, 2);
    outFile = reportJsonFile;
    break;
  case 'html':
    outData = templates.mochawesome(inData);
    outFile = reportHtmlFile;
    outMsg = "\nopen " + outFile.replace(process.cwd(),'').replace('/', '') + "\n";
    break;
  case 'css':
    outData = inData;
    outFile = reportCssFile;
    break;
  }

  try {
    if (!fs.existsSync(reportsDir)) {
      fs.mkdirSync(reportsDir);
    };
    writeFile = fs.openSync(outFile, 'w');
    fs.writeSync(writeFile, outData);
    fs.close(writeFile);
    util.print("Saved " + outFile + "\n" + outMsg);

  } catch (err) {
    util.print("\nError: Unable to save " + outFile + "\n" + err);
  }
}

/**
 * Register Handlebars helpers used in template
 *
 * @api private
 */

function initHandlebarsHelpers () {
  handlebars.registerHelper('toSeconds', function (context) {
    return context/1000;
  });

  handlebars.registerHelper('dateFormat', function(context, format) {
    if (format === "fromNow") {
      return moment(context).fromNow();
    } else {
      return moment(context).format(format);
    }
  });
}<|endoftext|>


<commit_before>var _ = require('lodash');
var Route = require('./route-model');
var Variant = require('./variant-model');
var Plugin = require('./plugin-model');
var util = require('./util');

var lastSmocksInstance;
function createSmocksInstance (id) {
  var _routes = [];
  var _plugins = [];
  var _variants = {};
  var _profiles = {};
  var _actions = {};
  var _connection;

  var smocksInstance = lastSmocksInstance = module.exports.lastSmocksInstance = {
    id: function () {
      return id;
    },

    connection: function (connection) {
      if (connection) {
        _connection = connection;
      }
      return _connection;
    },

    route: function (data) {
      if (!data.path) {
        throw new Error('Routes must be in the form of {path: "...", method: "..."}');
      } else {
        var route = new Route(data, smocksInstance);
        _routes.push(route);
        return route;
      }
    },

    method: function (route, method) {
      if (route.hasVariants()) {
        // we need a new route
        var _route = smocksInstance.route({ path: route.path });
        _route._method = method;
        return _route;
      } else {
        // we can repurpose the current route
        route._method = method;
        return route;
      }
    },

    variant: function (data) {
      var variant = new Variant(data, smocksInstance);
      _variants[variant.id()] = variant;
      return variant;
    },

    profile: function (id, profile) {
      _profiles[id] = profile;
    },

    action: function (id, options) {
      if (!options) {
        options = id;
        id = options.id;
      } else {
        options.id = id;
      }

      _actions[id] = options;
      return smocksInstance;
    },

    actions: {
      get: function () {
        return _actions;
      },
      execute: function (id, input, request) {
        var action = _actions[id];
        if (!action) {
          return false;
        } else {
          return util.executionContext({
            request: request,
            smocks: smocksInstance
          }, function (context) {
            action.handler.call(context, input);
            return true;
          });
        }
      }
    },

    profiles: {
      applyProfile: function (profile, request) {
        if (_.isString(profile)) {
          profile = _profiles[profile];
        }
        if (profile) {
          // reset the state first
          smocksInstance.state.resetRouteState(request);
          _.each(_routes, function (route) {
            route.applyProfile((route._id && profile[route._id]) || {}, request);
          });

          // FIXME we're only resetting global plugin state where we should be saving that in a profile
          smocksInstance.plugins.resetInput(request);
          return true;
        } else {
          return false;
        }
      },

      get: function (id) {
        if (!id) {
          return _profiles;
        }
        return _profiles[id];
      }
    },

    plugin: function (data) {
      var plugin = new Plugin(data, smocksInstance);
      if (plugin.plugin) {
        plugin.plugin(smocksInstance);
      }
      _plugins.push(plugin);
      return smocksInstance;
    },

    plugins: {
      get: function () {
        return _plugins;
      },

      resetInput: function (request) {
        var state = smocksInstance.state.routeState(request);
        var pluginState = state._pluginState = {};
        _.each(_plugins, function (plugin) {
          var input = plugin.input();
          if (input) {
            pluginState[plugin.id()] = {};
            _.each(input, function (data, id) {
              smocksInstance.plugins.updateInput(plugin.id(), id, data.defaultValue, request);
            });
          }
        });
      },

      updateInput: function (pluginId, id, value, request) {
        var input = smocksInstance.state.routeState(request)._pluginState;
        var pluginInput = input[pluginId];
        if (!pluginInput) {
          pluginInput = {};
          input[pluginId] = pluginInput;
        }
        pluginInput[id] = value;
      },

      getInput: function (request) {
        return smocksInstance.state.routeState(request)._pluginState;
      },

      getInputValue: function (pluginId, id, request) {
        var input = smocksInstance.state.routeState(request)._pluginState[pluginId];
        return input && input[id];
      }
    },

    routes: {
      get: function (id) {
        if (!id) {
          return _routes;
        }
        for (var i=0; i<_routes.length; i++) {
          if (_routes[i].id() === id) {
            return _routes[i];
          }
        }
      },
    },

    variants: {
      get: function (id) {
        if (!id) {
          return _.map(_variants, function (variant) { return variant; });
        }
        return _variants[id];
      }
    },

    global: function () {
      return smocksInstance;
    },

    done: function () {
      return smocksInstance;
    },

    findRoute: function (id) {
      return _.find(_routes, function (route) {
        return route._id === id;
      });
    },

    _sanitizeOptions: function (options) {
      options = _.clone(options || {});
      if (options.state) {
        if (options.state === 'cookie' || options.state === 'request') {
          var CookieState = require('./state/cookie-state');
          options.state = new CookieState();
        } else if (options.state === 'static') {
          options.state = require('./state/static-state');
        }
        if (!options.state.initialize) {
          console.error('state handler *must* implement "initialize" method: ', options.state);
          process.exit(1);
        }
      } else {
        options.state = require('./state/static-state');
      }

      return options;
    },

    _sanityCheckRoutes: function () {
      var routeIndex = {};
      _.each(_routes, function (route) {
        var id = route.id();
        if (routeIndex[id]) {
          console.error('duplicate route key "' + id + '"');
          process.exit(1);
        } else {
          routeIndex[id] = true;
        }

        var variants = route.variants();
        var variantIndex = {};
        _.each(variants, function (variant) {
          id = variant.id();
          if (variantIndex[id]) {
            console.error('duplicate variant key "' + id + '" for route "' + route.id() + '"');
            process.exit(1);
          } else {
            variantIndex[id] = true;
          }
        });
      });
    }
  };

  smocksInstance.db = require('./datastore')({
    defaultStore: function smocksDbStore () {
      var context = smocksInstance.context;
      if (context) {
        var store = context.state('db');
        if (!store) {
          store = {};
          context.state('db', store);
        }
        return store;
      }
    }
  });

  require('./plugins/har-viewer-plugin')(smocksInstance);
  require('./plugins/proxy-plugin')(smocksInstance);

  require('./hapi')(smocksInstance);

  return smocksInstance;
}

// allow for backwards compatibility
['connection', 'route', 'variant', 'profile', 'action', 'plugin', 'global', 'findRoute'].forEach(function (key) {
  module.exports[key] = function () {
    if (!lastSmocksInstance) {
      throw new Error('you must call smocks(_id_) before calling `' + key + '`');
    }
    return lastSmocksInstance[key].apply(lastSmocksInstance, arguments);
  };
});

module.exports = createSmocksInstance;
<commit_msg>fix default 'static' state<commit_after>var _ = require('lodash');
var Route = require('./route-model');
var Variant = require('./variant-model');
var Plugin = require('./plugin-model');
var util = require('./util');

var lastSmocksInstance;
function createSmocksInstance (id) {
  var _routes = [];
  var _plugins = [];
  var _variants = {};
  var _profiles = {};
  var _actions = {};
  var _connection;

  var smocksInstance = lastSmocksInstance = module.exports.lastSmocksInstance = {
    id: function () {
      return id;
    },

    connection: function (connection) {
      if (connection) {
        _connection = connection;
      }
      return _connection;
    },

    route: function (data) {
      if (!data.path) {
        throw new Error('Routes must be in the form of {path: "...", method: "..."}');
      } else {
        var route = new Route(data, smocksInstance);
        _routes.push(route);
        return route;
      }
    },

    method: function (route, method) {
      if (route.hasVariants()) {
        // we need a new route
        var _route = smocksInstance.route({ path: route.path });
        _route._method = method;
        return _route;
      } else {
        // we can repurpose the current route
        route._method = method;
        return route;
      }
    },

    variant: function (data) {
      var variant = new Variant(data, smocksInstance);
      _variants[variant.id()] = variant;
      return variant;
    },

    profile: function (id, profile) {
      _profiles[id] = profile;
    },

    action: function (id, options) {
      if (!options) {
        options = id;
        id = options.id;
      } else {
        options.id = id;
      }

      _actions[id] = options;
      return smocksInstance;
    },

    actions: {
      get: function () {
        return _actions;
      },
      execute: function (id, input, request) {
        var action = _actions[id];
        if (!action) {
          return false;
        } else {
          return util.executionContext({
            request: request,
            smocks: smocksInstance
          }, function (context) {
            action.handler.call(context, input);
            return true;
          });
        }
      }
    },

    profiles: {
      applyProfile: function (profile, request) {
        if (_.isString(profile)) {
          profile = _profiles[profile];
        }
        if (profile) {
          // reset the state first
          smocksInstance.state.resetRouteState(request);
          _.each(_routes, function (route) {
            route.applyProfile((route._id && profile[route._id]) || {}, request);
          });

          // FIXME we're only resetting global plugin state where we should be saving that in a profile
          smocksInstance.plugins.resetInput(request);
          return true;
        } else {
          return false;
        }
      },

      get: function (id) {
        if (!id) {
          return _profiles;
        }
        return _profiles[id];
      }
    },

    plugin: function (data) {
      var plugin = new Plugin(data, smocksInstance);
      if (plugin.plugin) {
        plugin.plugin(smocksInstance);
      }
      _plugins.push(plugin);
      return smocksInstance;
    },

    plugins: {
      get: function () {
        return _plugins;
      },

      resetInput: function (request) {
        var state = smocksInstance.state.routeState(request);
        var pluginState = state._pluginState = {};
        _.each(_plugins, function (plugin) {
          var input = plugin.input();
          if (input) {
            pluginState[plugin.id()] = {};
            _.each(input, function (data, id) {
              smocksInstance.plugins.updateInput(plugin.id(), id, data.defaultValue, request);
            });
          }
        });
      },

      updateInput: function (pluginId, id, value, request) {
        var input = smocksInstance.state.routeState(request)._pluginState;
        var pluginInput = input[pluginId];
        if (!pluginInput) {
          pluginInput = {};
          input[pluginId] = pluginInput;
        }
        pluginInput[id] = value;
      },

      getInput: function (request) {
        return smocksInstance.state.routeState(request)._pluginState;
      },

      getInputValue: function (pluginId, id, request) {
        var input = smocksInstance.state.routeState(request)._pluginState[pluginId];
        return input && input[id];
      }
    },

    routes: {
      get: function (id) {
        if (!id) {
          return _routes;
        }
        for (var i=0; i<_routes.length; i++) {
          if (_routes[i].id() === id) {
            return _routes[i];
          }
        }
      },
    },

    variants: {
      get: function (id) {
        if (!id) {
          return _.map(_variants, function (variant) { return variant; });
        }
        return _variants[id];
      }
    },

    global: function () {
      return smocksInstance;
    },

    done: function () {
      return smocksInstance;
    },

    findRoute: function (id) {
      return _.find(_routes, function (route) {
        return route._id === id;
      });
    },

    _sanitizeOptions: function (options) {
      options = _.clone(options || {});
      if (options.state) {
        if (options.state === 'cookie' || options.state === 'request') {
          var CookieState = require('./state/cookie-state');
          options.state = new CookieState();
        } else if (options.state === 'static') {
          options.state = require('./state/static-state')();
        }
        if (!options.state.initialize) {
          console.error('state handler *must* implement "initialize" method: ', options.state);
          process.exit(1);
        }
      } else {
        options.state = require('./state/static-state')();
      }

      return options;
    },

    _sanityCheckRoutes: function () {
      var routeIndex = {};
      _.each(_routes, function (route) {
        var id = route.id();
        if (routeIndex[id]) {
          console.error('duplicate route key "' + id + '"');
          process.exit(1);
        } else {
          routeIndex[id] = true;
        }

        var variants = route.variants();
        var variantIndex = {};
        _.each(variants, function (variant) {
          id = variant.id();
          if (variantIndex[id]) {
            console.error('duplicate variant key "' + id + '" for route "' + route.id() + '"');
            process.exit(1);
          } else {
            variantIndex[id] = true;
          }
        });
      });
    }
  };

  smocksInstance.db = require('./datastore')({
    defaultStore: function smocksDbStore () {
      var context = smocksInstance.context;
      if (context) {
        var store = context.state('db');
        if (!store) {
          store = {};
          context.state('db', store);
        }
        return store;
      }
    }
  });

  require('./plugins/har-viewer-plugin')(smocksInstance);
  require('./plugins/proxy-plugin')(smocksInstance);

  require('./hapi')(smocksInstance);

  return smocksInstance;
}

// allow for backwards compatibility
['connection', 'route', 'variant', 'profile', 'action', 'plugin', 'global', 'findRoute'].forEach(function (key) {
  module.exports[key] = function () {
    if (!lastSmocksInstance) {
      throw new Error('you must call smocks(_id_) before calling `' + key + '`');
    }
    return lastSmocksInstance[key].apply(lastSmocksInstance, arguments);
  };
});

module.exports = createSmocksInstance;
<|endoftext|>


<commit_before>/*
 * Node module wrapper for the PhantomJS script
 */

'use strict'

var fs = require('fs')
var tmp = require('tmp')
var path = require('path')
var spawn = require('child_process').spawn
var phantomjs = require('phantomjs-prebuilt')
var phantomJsBinPath = phantomjs.path
var apartment = require('apartment')
var cssAstFormatter = require('css')
var osTmpdir = require('os-tmpdir')
var postformatting = require('./postformatting/')
var normalizeCss = require('./normalize-css')

// for phantomjs
var configString = '--config=' + path.join(__dirname, 'phantomjs', 'config.json')
var script = path.join(__dirname, 'phantomjs', 'core.js')

var DEFAULT_VIEWPORT_WIDTH = 1300 // px
var DEFAULT_VIEWPORT_HEIGHT = 900 // px
var DEFAULT_TIMEOUT = 30000 // ms
var DEFAULT_MAX_EMBEDDED_BASE64_LENGTH = 1000 // chars
var DEFAULT_USER_AGENT = 'Penthouse Critical Path CSS Generator'
var TMP_DIR = osTmpdir()
var DEFAULT_RENDER_WAIT_TIMEOUT = 100
var DEFAULT_BLOCK_JS_REQUESTS = true

var toPhantomJsOptions = function (maybeOptionsHash) {
  if (typeof maybeOptionsHash !== 'object') {
    return [];
  }
  return Object.keys(maybeOptionsHash).map(function (optName) {
    return '--' + optName + '=' + maybeOptionsHash[optName];
  })
}

function penthouseScriptArgs (options, ast) {
  // need to annotate forceInclude values to allow RegExp to pass through JSON serialization
  var forceInclude = (options.forceInclude || []).map(function (forceIncludeValue) {
    if (typeof forceIncludeValue === 'object' && forceIncludeValue.constructor.name === 'RegExp') {
      return { type: 'RegExp', value: forceIncludeValue.source }
    }
    return { value: forceIncludeValue }
  })
  return [
    options.url || '',
    writeAstToFile(ast),
    options.width || DEFAULT_VIEWPORT_WIDTH,
    options.height || DEFAULT_VIEWPORT_HEIGHT,
    JSON.stringify(forceInclude), // stringify to maintain array
    options.userAgent || DEFAULT_USER_AGENT,
    options.renderWaitTime || DEFAULT_RENDER_WAIT_TIMEOUT,
    (typeof options.blockJSRequests !== 'undefined' ? options.blockJSRequests : DEFAULT_BLOCK_JS_REQUESTS),
    m.DEBUG
  ]
}

function writeAstToFile (ast) {
  // save ast to file
  var tmpobj = tmp.fileSync({dir: TMP_DIR})
  fs.writeFileSync(tmpobj.name, JSON.stringify(ast))
  return tmpobj.name
}

var m = module.exports = function (options, callback) { // jshint ignore: line
  var stdOut = ''
  var stdErr = ''
  // debugging
  var START_TIME = Date.now()
  var debuglog = function (msg, isError) {
    if (m.DEBUG) {
      var errMsg = 'time: ' + (Date.now() - START_TIME) + ' | ' + (isError ? 'ERR: ' : '') + msg
      stdErr += errMsg
      console.error(errMsg)
    }
  }
  normalizeCss.DEBUG = m.DEBUG

  function generateCriticalCss (ast) {
    var debuggingHelp = '',
        cp,
        killTimeout

    var timeoutWait = options.timeout || DEFAULT_TIMEOUT

    var scriptArgs = penthouseScriptArgs(options, ast)

    var phantomJsArgs = [configString].concat(toPhantomJsOptions(options.phantomJsOptions))
    phantomJsArgs.push(script)
    phantomJsArgs = phantomJsArgs.concat(scriptArgs)

    cp = spawn(phantomJsBinPath, phantomJsArgs)

    // Errors arise before the process starts
    cp.on('error', function (err) {
      debuggingHelp += 'Error executing penthouse using ' + phantomJsBinPath
      debuggingHelp += err.stack
      err.debug = debuggingHelp
      callback(err)
    })

    cp.stdout.on('data', function (data) {
      stdOut += data
    })

    cp.stderr.on('data', function (data) {
      stdErr += data
      debuglog(String(data))
    })

    cp.on('close', function (code) {
      if (code !== 0) {
        debuggingHelp += 'PhantomJS process closed with code ' + code
      }
    })

    // kill after timeout
    killTimeout = setTimeout(function () {
      var msg = 'Penthouse timed out after ' + timeoutWait / 1000 + 's. '
      debuggingHelp += msg
      stdErr += msg
      cp.kill('SIGTERM')
    }, timeoutWait)

    cp.on('exit', function (code) {
      if (code === 0) {
        // promise purely for catching errors,
        // that otherwise exit node
        new Promise(function (resolve) {
          var finalCss = postformatting(stdOut, {
            maxEmbeddedBase64Length: typeof options.maxEmbeddedBase64Length === 'number' ? options.maxEmbeddedBase64Length : DEFAULT_MAX_EMBEDDED_BASE64_LENGTH
          }, m.DEBUG, START_TIME)

          if (finalCss.trim().length === 0) {
            // TODO: this error should surface to user
            debuglog('Note: Generated critical css was empty for URL: ' + options.url)
          } else {
            // remove irrelevant css properties
            finalCss = apartment(finalCss, {
              properties: [
                '(.*)transition(.*)',
                'cursor',
                'pointer-events',
                '(-webkit-)?tap-highlight-color',
                '(.*)user-select'
              ],
              // TODO: move into core phantomjs script
              selectors: [
                '::(-moz-)?selection'
              ]
            })
          }
          callback(null, finalCss)
          resolve()
          return
        })
        .catch(function (err) {
          console.log('caught err', err)
          callback(err)
        })
      } else {
        debuggingHelp += 'PhantomJS process exited with code ' + code
        var err = new Error(stdErr + stdOut)
        err.code = code
        err.debug = debuggingHelp
        err.stdout = stdOut
        err.stderr = stdErr
        callback(err)
      }
      // we're done here - clean up
      clearTimeout(killTimeout)
      // can't rely on that the parent process will be terminated any time soon,
      // need to rm listeners and kill child process manually
      process.removeListener('exit', exitHandler)
      process.removeListener('SIGTERM', sigtermHandler)
      cp.kill('SIGTERM')
    })

    function exitHandler () {
      cp.kill('SIGTERM')
    }
    function sigtermHandler () {
      cp.kill('SIGTERM')
      process.exit(0)
    }
    process.on('exit', exitHandler)
    process.on('SIGTERM', sigtermHandler)
  }

  function generateAstFromCssFile(cssfilepath) {
    // read the css and parse the ast
    // if errors, normalize css and try again
    // only then pass css to penthouse
    return new Promise(function (resolve, reject) {
      var css
      try {
        css = fs.readFileSync(cssfilepath, 'utf8')
      } catch (e) {
        reject(e.message)
        return
      }
      debuglog('opened css file')

      var ast = cssAstFormatter.parse(css, { silent: true })
      var parsingErrors = ast.stylesheet.parsingErrors.filter(function (err) {
        // the forked version of the astParser used fixes these errors itself
        return err.reason !== 'Extra closing brace'
      })
      if (parsingErrors.length === 0) {
        debuglog('parsed ast (without errors)')
        resolve(ast)
        return
      }

      // had breaking parsing errors
      // NOTE: only informing about first error, even if there were more than one.
      var parsingErrorMessage = parsingErrors[0].message
      if (options.strict === true) {
        reject(parsingErrorMessage)
        return
      }

      debuglog('Failed ast formatting css \'' + parsingErrorMessage + '\': ')
      var normalizeScriptArgs = [
        options.url || '',
        options.css || '',
        options.userAgent || DEFAULT_USER_AGENT,
        m.DEBUG
      ]
      normalizeCss(
        normalizeScriptArgs,
        function (err, normalizedCss) {
          debuglog('normalized css: ' + (normalizedCss ? normalizedCss.length : typeof normalizedCss))
          ast = cssAstFormatter.parse(normalizedCss, { silent: true })
          debuglog('parsed normalised css into ast')
          var parsingErrors = ast.stylesheet.parsingErrors.filter(function (err) {
            // the forked version of the astParser used fixes these errors itself
            return err.reason !== 'Extra closing brace'
          })
          if (parsingErrors.length > 0) {
            debuglog('..with parsingErrors: ' + parsingErrors[0].reason)
          }
          resolve(ast)
          return
        }
      )
    })
  }

  generateAstFromCssFile(options.css)
  .then(generateCriticalCss)
  .catch(callback)
}
<commit_msg>remove stray console.log<commit_after>/*
 * Node module wrapper for the PhantomJS script
 */

'use strict'

var fs = require('fs')
var tmp = require('tmp')
var path = require('path')
var spawn = require('child_process').spawn
var phantomjs = require('phantomjs-prebuilt')
var phantomJsBinPath = phantomjs.path
var apartment = require('apartment')
var cssAstFormatter = require('css')
var osTmpdir = require('os-tmpdir')
var postformatting = require('./postformatting/')
var normalizeCss = require('./normalize-css')

// for phantomjs
var configString = '--config=' + path.join(__dirname, 'phantomjs', 'config.json')
var script = path.join(__dirname, 'phantomjs', 'core.js')

var DEFAULT_VIEWPORT_WIDTH = 1300 // px
var DEFAULT_VIEWPORT_HEIGHT = 900 // px
var DEFAULT_TIMEOUT = 30000 // ms
var DEFAULT_MAX_EMBEDDED_BASE64_LENGTH = 1000 // chars
var DEFAULT_USER_AGENT = 'Penthouse Critical Path CSS Generator'
var TMP_DIR = osTmpdir()
var DEFAULT_RENDER_WAIT_TIMEOUT = 100
var DEFAULT_BLOCK_JS_REQUESTS = true

var toPhantomJsOptions = function (maybeOptionsHash) {
  if (typeof maybeOptionsHash !== 'object') {
    return [];
  }
  return Object.keys(maybeOptionsHash).map(function (optName) {
    return '--' + optName + '=' + maybeOptionsHash[optName];
  })
}

function penthouseScriptArgs (options, ast) {
  // need to annotate forceInclude values to allow RegExp to pass through JSON serialization
  var forceInclude = (options.forceInclude || []).map(function (forceIncludeValue) {
    if (typeof forceIncludeValue === 'object' && forceIncludeValue.constructor.name === 'RegExp') {
      return { type: 'RegExp', value: forceIncludeValue.source }
    }
    return { value: forceIncludeValue }
  })
  return [
    options.url || '',
    writeAstToFile(ast),
    options.width || DEFAULT_VIEWPORT_WIDTH,
    options.height || DEFAULT_VIEWPORT_HEIGHT,
    JSON.stringify(forceInclude), // stringify to maintain array
    options.userAgent || DEFAULT_USER_AGENT,
    options.renderWaitTime || DEFAULT_RENDER_WAIT_TIMEOUT,
    (typeof options.blockJSRequests !== 'undefined' ? options.blockJSRequests : DEFAULT_BLOCK_JS_REQUESTS),
    m.DEBUG
  ]
}

function writeAstToFile (ast) {
  // save ast to file
  var tmpobj = tmp.fileSync({dir: TMP_DIR})
  fs.writeFileSync(tmpobj.name, JSON.stringify(ast))
  return tmpobj.name
}

var m = module.exports = function (options, callback) { // jshint ignore: line
  var stdOut = ''
  var stdErr = ''
  // debugging
  var START_TIME = Date.now()
  var debuglog = function (msg, isError) {
    if (m.DEBUG) {
      var errMsg = 'time: ' + (Date.now() - START_TIME) + ' | ' + (isError ? 'ERR: ' : '') + msg
      stdErr += errMsg
      console.error(errMsg)
    }
  }
  normalizeCss.DEBUG = m.DEBUG

  function generateCriticalCss (ast) {
    var debuggingHelp = '',
        cp,
        killTimeout

    var timeoutWait = options.timeout || DEFAULT_TIMEOUT

    var scriptArgs = penthouseScriptArgs(options, ast)

    var phantomJsArgs = [configString].concat(toPhantomJsOptions(options.phantomJsOptions))
    phantomJsArgs.push(script)
    phantomJsArgs = phantomJsArgs.concat(scriptArgs)

    cp = spawn(phantomJsBinPath, phantomJsArgs)

    // Errors arise before the process starts
    cp.on('error', function (err) {
      debuggingHelp += 'Error executing penthouse using ' + phantomJsBinPath
      debuggingHelp += err.stack
      err.debug = debuggingHelp
      callback(err)
    })

    cp.stdout.on('data', function (data) {
      stdOut += data
    })

    cp.stderr.on('data', function (data) {
      stdErr += data
      debuglog(String(data))
    })

    cp.on('close', function (code) {
      if (code !== 0) {
        debuggingHelp += 'PhantomJS process closed with code ' + code
      }
    })

    // kill after timeout
    killTimeout = setTimeout(function () {
      var msg = 'Penthouse timed out after ' + timeoutWait / 1000 + 's. '
      debuggingHelp += msg
      stdErr += msg
      cp.kill('SIGTERM')
    }, timeoutWait)

    cp.on('exit', function (code) {
      if (code === 0) {
        // promise purely for catching errors,
        // that otherwise exit node
        new Promise(function (resolve) {
          var finalCss = postformatting(stdOut, {
            maxEmbeddedBase64Length: typeof options.maxEmbeddedBase64Length === 'number' ? options.maxEmbeddedBase64Length : DEFAULT_MAX_EMBEDDED_BASE64_LENGTH
          }, m.DEBUG, START_TIME)

          if (finalCss.trim().length === 0) {
            // TODO: this error should surface to user
            debuglog('Note: Generated critical css was empty for URL: ' + options.url)
          } else {
            // remove irrelevant css properties
            finalCss = apartment(finalCss, {
              properties: [
                '(.*)transition(.*)',
                'cursor',
                'pointer-events',
                '(-webkit-)?tap-highlight-color',
                '(.*)user-select'
              ],
              // TODO: move into core phantomjs script
              selectors: [
                '::(-moz-)?selection'
              ]
            })
          }
          callback(null, finalCss)
          resolve()
          return
        })
        .catch(function (err) {
          callback(err)
        })
      } else {
        debuggingHelp += 'PhantomJS process exited with code ' + code
        var err = new Error(stdErr + stdOut)
        err.code = code
        err.debug = debuggingHelp
        err.stdout = stdOut
        err.stderr = stdErr
        callback(err)
      }
      // we're done here - clean up
      clearTimeout(killTimeout)
      // can't rely on that the parent process will be terminated any time soon,
      // need to rm listeners and kill child process manually
      process.removeListener('exit', exitHandler)
      process.removeListener('SIGTERM', sigtermHandler)
      cp.kill('SIGTERM')
    })

    function exitHandler () {
      cp.kill('SIGTERM')
    }
    function sigtermHandler () {
      cp.kill('SIGTERM')
      process.exit(0)
    }
    process.on('exit', exitHandler)
    process.on('SIGTERM', sigtermHandler)
  }

  function generateAstFromCssFile(cssfilepath) {
    // read the css and parse the ast
    // if errors, normalize css and try again
    // only then pass css to penthouse
    return new Promise(function (resolve, reject) {
      var css
      try {
        css = fs.readFileSync(cssfilepath, 'utf8')
      } catch (e) {
        reject(e.message)
        return
      }
      debuglog('opened css file')

      var ast = cssAstFormatter.parse(css, { silent: true })
      var parsingErrors = ast.stylesheet.parsingErrors.filter(function (err) {
        // the forked version of the astParser used fixes these errors itself
        return err.reason !== 'Extra closing brace'
      })
      if (parsingErrors.length === 0) {
        debuglog('parsed ast (without errors)')
        resolve(ast)
        return
      }

      // had breaking parsing errors
      // NOTE: only informing about first error, even if there were more than one.
      var parsingErrorMessage = parsingErrors[0].message
      if (options.strict === true) {
        reject(parsingErrorMessage)
        return
      }

      debuglog('Failed ast formatting css \'' + parsingErrorMessage + '\': ')
      var normalizeScriptArgs = [
        options.url || '',
        options.css || '',
        options.userAgent || DEFAULT_USER_AGENT,
        m.DEBUG
      ]
      normalizeCss(
        normalizeScriptArgs,
        function (err, normalizedCss) {
          debuglog('normalized css: ' + (normalizedCss ? normalizedCss.length : typeof normalizedCss))
          ast = cssAstFormatter.parse(normalizedCss, { silent: true })
          debuglog('parsed normalised css into ast')
          var parsingErrors = ast.stylesheet.parsingErrors.filter(function (err) {
            // the forked version of the astParser used fixes these errors itself
            return err.reason !== 'Extra closing brace'
          })
          if (parsingErrors.length > 0) {
            debuglog('..with parsingErrors: ' + parsingErrors[0].reason)
          }
          resolve(ast)
          return
        }
      )
    })
  }

  generateAstFromCssFile(options.css)
  .then(generateCriticalCss)
  .catch(callback)
}
<|endoftext|>


<commit_before>/*
 * Node module wrapper for the PhantomJS script
 */

var path = require('path')
var spawn = require('child_process').spawn
var phantomjs = require('phantomjs')
var phantomJsBinPath = phantomjs.path
var configString = ('--config=' + path.join(__dirname, 'phantomjs', 'config.json'))
var script = path.join(__dirname, 'phantomjs', 'core.js')

var DEFAULT_VIEWPORT_WIDTH = 1300 // px
var DEFAULT_VIEWPORT_HEIGHT = 900 // px
var DEFAULT_TIMEOUT = 30000 // ms
var DEFAULT_MAX_EMBEDDED_BASE64_LENGTH = 1000 // chars

var removePhantomJSSecurityErrors = function (stdOut) {
  stdOut = stdOut.replace('Unsafe JavaScript attempt to access frame with URL about:blank from frame with URL ', '')
  stdOut = stdOut.replace(/file:\/\/.*core.js\./, '')
  stdOut = stdOut.replace(' Domains, protocols and ports must match.', '')
  return stdOut
}

var m = module.exports = function (options, callback) {
  var stdOut = '',
    stdErr = '',
    debuggingHelp = '',
    cp

  // need to annotate forceInclude values to allow RegExp to pass through JSON serialization
  var forceInclude = (options.forceInclude || []).map(function (forceIncludeValue) {
    if (typeof forceIncludeValue === 'object' && forceIncludeValue.constructor.name === 'RegExp') {
      return { type: 'RegExp', value: forceIncludeValue.source }
    }
    return { value: forceIncludeValue }
  })
  // set the options, falling back to defaults
  var scriptArgs = [
    options.url || '',
    options.css || '',
    options.width || DEFAULT_VIEWPORT_WIDTH,
    options.height || DEFAULT_VIEWPORT_HEIGHT,
    JSON.stringify(forceInclude), // stringify to maintain array
    !!options.strict,
    typeof options.maxEmbeddedBase64Length === 'number' ? options.maxEmbeddedBase64Length : DEFAULT_MAX_EMBEDDED_BASE64_LENGTH
  ]

  cp = spawn(phantomJsBinPath, [configString, script].concat(scriptArgs))

  // Errors arise before the process starts
  cp.on('error', function (err) {
    debuggingHelp += 'Error executing penthouse using ' + phantomJsBinPath
    debuggingHelp += err.stack
    err.debug = debuggingHelp
    callback(err)
  })

  cp.stdout.on('data', function (data) {
    stdOut += data
    if (m.DEBUG) { console.log('' + data) }
  })

  cp.stderr.on('data', function (data) {
    stdErr += data
    if (m.DEBUG) console.error('' + data)
  })

  cp.on('close', function (code) {
    if (code !== 0) {
      debuggingHelp += 'PhantomJS process closed with code ' + code
    }
  })

  cp.on('exit', function (code) {
    if (code === 0) {
      if (m.DEBUG) {
        console.log('stdout: ' + stdOut)
        console.log('stderr: ' + stdErr)
      }
      stdOut = removePhantomJSSecurityErrors(stdOut)
      callback(null, stdOut)
    } else {
      debuggingHelp += 'PhantomJS process exited with code ' + code
      var err = new Error(stdErr + stdOut)
      err.code = code
      err.debug = debuggingHelp
      err.stdout = stdOut
      err.stderr = stdErr
      callback(err)
    }
  })

  // kill after timeout
  var timeout = options.timeout || DEFAULT_TIMEOUT
  setTimeout(function () {
    var msg = 'Penthouse timed out after ' + timeout / 1000 + 's. '
    debuggingHelp += msg
    stdErr += msg
    cp.kill('SIGTERM')
  }, timeout)

  process.on('exit', function () {
    cp.kill('SIGTERM')
  })

  process.on('SIGTERM', function () {
    cp.kill('SIGTERM')
    process.exit(0)
  })

}
<commit_msg>clear timeout on exit<commit_after>/*
 * Node module wrapper for the PhantomJS script
 */

var path = require('path')
var spawn = require('child_process').spawn
var phantomjs = require('phantomjs')
var phantomJsBinPath = phantomjs.path
var configString = ('--config=' + path.join(__dirname, 'phantomjs', 'config.json'))
var script = path.join(__dirname, 'phantomjs', 'core.js')

var DEFAULT_VIEWPORT_WIDTH = 1300 // px
var DEFAULT_VIEWPORT_HEIGHT = 900 // px
var DEFAULT_TIMEOUT = 30000 // ms
var DEFAULT_MAX_EMBEDDED_BASE64_LENGTH = 1000 // chars

var removePhantomJSSecurityErrors = function (stdOut) {
  stdOut = stdOut.replace('Unsafe JavaScript attempt to access frame with URL about:blank from frame with URL ', '')
  stdOut = stdOut.replace(/file:\/\/.*core.js\./, '')
  stdOut = stdOut.replace(' Domains, protocols and ports must match.', '')
  return stdOut
}

var m = module.exports = function (options, callback) {
  var stdOut = '',
    stdErr = '',
    debuggingHelp = '',
    cp

  // need to annotate forceInclude values to allow RegExp to pass through JSON serialization
  var forceInclude = (options.forceInclude || []).map(function (forceIncludeValue) {
    if (typeof forceIncludeValue === 'object' && forceIncludeValue.constructor.name === 'RegExp') {
      return { type: 'RegExp', value: forceIncludeValue.source }
    }
    return { value: forceIncludeValue }
  })
  // set the options, falling back to defaults
  var scriptArgs = [
    options.url || '',
    options.css || '',
    options.width || DEFAULT_VIEWPORT_WIDTH,
    options.height || DEFAULT_VIEWPORT_HEIGHT,
    JSON.stringify(forceInclude), // stringify to maintain array
    !!options.strict,
    typeof options.maxEmbeddedBase64Length === 'number' ? options.maxEmbeddedBase64Length : DEFAULT_MAX_EMBEDDED_BASE64_LENGTH
  ]

  cp = spawn(phantomJsBinPath, [configString, script].concat(scriptArgs))

  // Errors arise before the process starts
  cp.on('error', function (err) {
    debuggingHelp += 'Error executing penthouse using ' + phantomJsBinPath
    debuggingHelp += err.stack
    err.debug = debuggingHelp
    callback(err)
  })

  cp.stdout.on('data', function (data) {
    stdOut += data
    if (m.DEBUG) { console.log('' + data) }
  })

  cp.stderr.on('data', function (data) {
    stdErr += data
    if (m.DEBUG) console.error('' + data)
  })

  cp.on('close', function (code) {
    if (code !== 0) {
      debuggingHelp += 'PhantomJS process closed with code ' + code
    }
  })

  // kill after timeout
  var timeout = options.timeout || DEFAULT_TIMEOUT
  var killTimeout = setTimeout(function () {
    var msg = 'Penthouse timed out after ' + timeout / 1000 + 's. '
    debuggingHelp += msg
    stdErr += msg
    cp.kill('SIGTERM')
  }, timeout)

  cp.on('exit', function (code) {
    if (killTimeout) {
      clearTimeout(killTimeout);
    }
    if (code === 0) {
      if (m.DEBUG) {
        console.log('stdout: ' + stdOut)
        console.log('stderr: ' + stdErr)
      }
      stdOut = removePhantomJSSecurityErrors(stdOut)
      callback(null, stdOut)
    } else {
      debuggingHelp += 'PhantomJS process exited with code ' + code
      var err = new Error(stdErr + stdOut)
      err.code = code
      err.debug = debuggingHelp
      err.stdout = stdOut
      err.stderr = stdErr
      callback(err)
    }
  })

  process.on('exit', function () {
    cp.kill('SIGTERM')
  })

  process.on('SIGTERM', function () {
    cp.kill('SIGTERM')
    process.exit(0)
  })

}
<|endoftext|>


<commit_before>/* global document, window */

import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';

/**
 * Adapts focus styles based on the user's active input modality (i.e., how
 * they are interacting with the UI right now).
 *
 * Focus styles are only relevant when using the keyboard to interact with the
 * page. If we only show the focus ring when relevant, we can avoid user
 * confusion without compromising accessibility.
 *
 * The script uses two heuristics to determine whether the keyboard is being used:
 *
 * 1. a keydown event occurred immediately before a focus event;
 * 2. a focus event happened on an element which requires keyboard interaction (e.g., a text field);
 *
 * Based on https://github.com/WICG/modality
 */
const modality = () => {
  if (!canUseDOM) {
    return;
  }

  /**
   * Determine whether the keyboard is required when an element is focused
   */
  const proto = window.Element.prototype;
  const matcher = proto.matches ||
    proto.mozMatchesSelector ||
    proto.msMatchesSelector ||
    proto.webkitMatchesSelector;
  const keyboardModalityWhitelist = [
    'input:not([type])',
    'input[type=text]',
    'input[type=number]',
    'input[type=date]',
    'input[type=time]',
    'input[type=datetime]',
    'textarea',
    '[role=textbox]',
    // indicates that a custom element supports the keyboard
    '[supports-modality=keyboard]'
  ].join(',');

  const focusTriggersKeyboardModality = el => {
    if (matcher) {
      return matcher.call(el, keyboardModalityWhitelist) && matcher.call(el, ':not([readonly])');
    } else {
      return false;
    }
  };

  /**
   * Disable the focus ring by default
   */
  const id = 'react-native-modality';
  let styleElement = document.getElementById(id);
  if (!styleElement) {
    const style = `<style id="${id}">:focus { outline: none; }</style>`;
    document.head.insertAdjacentHTML('afterbegin', style);
    styleElement = document.getElementById(id);
  }

  const disableFocus = () => {
    if (styleElement) {
      styleElement.disabled = false;
    }
  };

  const enableFocus = () => {
    if (styleElement) {
      styleElement.disabled = true;
    }
  };

  /**
   * Manage the modality focus state
   */
  let keyboardTimer;
  let hadKeyboardEvent = false;

  // track when the keyboard is in use
  document.body.addEventListener(
    'keydown',
    () => {
      hadKeyboardEvent = true;
      if (keyboardTimer) {
        clearTimeout(keyboardTimer);
      }
      keyboardTimer = setTimeout(
        () => {
          hadKeyboardEvent = false;
        },
        100
      );
    },
    true
  );

  // disable focus style reset when the keyboard is in use
  document.body.addEventListener(
    'focus',
    e => {
      if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {
        enableFocus();
      }
    },
    true
  );

  // enable focus style reset when keyboard is no longer in use
  document.body.addEventListener(
    'blur',
    () => {
      if (!hadKeyboardEvent) {
        disableFocus();
      }
    },
    true
  );
};

export default modality;
<commit_msg>Update 'modality' implementation<commit_after>/* global document, window */

import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';

/**
 * Adapts focus styles based on the user's active input modality (i.e., how
 * they are interacting with the UI right now).
 *
 * Focus styles are only relevant when using the keyboard to interact with the
 * page. If we only show the focus ring when relevant, we can avoid user
 * confusion without compromising accessibility.
 *
 * The script uses two heuristics to determine whether the keyboard is being used:
 *
 * 1. a keydown event occurred immediately before a focus event;
 * 2. a focus event happened on an element which requires keyboard interaction (e.g., a text field);
 *
 * Based on https://github.com/WICG/focus-ring
 */
const modality = () => {
  if (!canUseDOM) {
    return;
  }

  let styleElement;
  let hadKeyboardEvent = false;
  let keyboardThrottleTimeoutID = 0;

  const proto = window.Element.prototype;
  const matches = proto.matches ||
    proto.mozMatchesSelector ||
    proto.msMatchesSelector ||
    proto.webkitMatchesSelector;

  // These elements should always have a focus ring drawn, because they are
  // associated with switching to a keyboard modality.
  const keyboardModalityWhitelist = [
    'input:not([type])',
    'input[type=text]',
    'input[type=search]',
    'input[type=url]',
    'input[type=tel]',
    'input[type=email]',
    'input[type=password]',
    'input[type=number]',
    'input[type=date]',
    'input[type=month]',
    'input[type=week]',
    'input[type=time]',
    'input[type=datetime]',
    'input[type=datetime-local]',
    'textarea',
    '[role=textbox]',
  ].join(',');

  /**
   * Disable the focus ring by default
   */
  const initialize = () => {
    // check if the style sheet needs to be created
    const id = 'react-native-modality';
    styleElement = document.getElementById(id);
    if (!styleElement) {
      // removes focus styles by default
      const style = `<style id="${id}">:focus { outline: none; }</style>`;
      document.head.insertAdjacentHTML('afterbegin', style);
      styleElement = document.getElementById(id);
    }
  }

  /**
   * Computes whether the given element should automatically trigger the
   * `focus-ring`.
   */
  const focusTriggersKeyboardModality = el => {
    if (matches) {
      return matches.call(el, keyboardModalityWhitelist) && matches.call(el, ':not([readonly])');
    } else {
      return false;
    }
  };

  /**
   * Add the focus ring to the focused element
   */
  const addFocusRing = () => {
    if (styleElement) {
      styleElement.disabled = true;
    }
  }

  /**
   * Remove the focus ring
   */
  const removeFocusRing = () => {
    if (styleElement) {
      styleElement.disabled = false;
    }
  };

  /**
   * On `keydown`, set `hadKeyboardEvent`, to be removed 100ms later if there
   * are no further keyboard events. The 100ms throttle handles cases where
   * focus is redirected programmatically after a keyboard event, such as
   * opening a menu or dialog.
   */
  const handleKeyDown = (e) => {
    hadKeyboardEvent = true;
    if (keyboardThrottleTimeoutID !== 0) {
      clearTimeout(keyboardThrottleTimeoutID);
    }
    keyboardThrottleTimeoutID = setTimeout(() => {
      hadKeyboardEvent = false;
      keyboardThrottleTimeoutID = 0;
    }, 100);
  };

  /**
   * Display the focus-ring when the keyboard was used to focus
   */
  const handleFocus = (e) => {
    if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {
      addFocusRing();
    }
  };

  /**
   * Remove the focus-ring when the keyboard was used to focus
   */
  const handleBlur = () => {
    if (!hadKeyboardEvent) {
      removeFocusRing();
    }
  };

  if (document.body && document.body.addEventListener) {
    initialize();
    document.body.addEventListener('keydown', handleKeyDown, true);
    document.body.addEventListener('focus', handleFocus, true);
    document.body.addEventListener('blur', handleBlur, true);
  }
};

export default modality;
<|endoftext|>


<commit_before>module.exports = function sleep(timeout) {
    return new Promise(
        function(resolve) {
            setTimeout(function() {
                resolve();
            }, timeout);
        }
    );
};
<commit_msg>Simplification to avoid one more function call.<commit_after>module.exports = function sleep(timeout) {
    return new Promise(function(resolve) {
        setTimeout(resolve, timeout);
    );
};
<|endoftext|>


<commit_before>/**
 * native-message.js
 */
"use strict";
{
  /* api */
  const os = require("os");
  const process = require("process");

  /* constants */
  const BYTE_LEN = 4;
  const CHAR = "utf8";
  const IS_BE = os.endianness() === "BE";

  /**
   * is function
   * @param {*} o - object to check
   * @returns {boolean} - result
   */
  const isFunction = o => typeof o === "function";

  /**
   * is string
   * @param {*} o - object to check
   * @returns {boolean} - result
   */
  const isString = o => typeof o === "string" || o instanceof String;

  /* Input */
  class Input {
    /**
     * decode message from buffer
     */
    constructor() {
      this._input;
      this._length;
    }

    /**
     * decode message
     * @returns {Array} - message array
     */
    _decodeMessage() {
      let arr = [];
      !this._length && this._input && this._input.length >= BYTE_LEN && (
        this._length = IS_BE && this._input.readUIntBE(0, BYTE_LEN) ||
                       this._input.readUIntLE(0, BYTE_LEN),
        this._input = this._input.slice(BYTE_LEN)
      );
      if (this._length && this._input && this._input.length >= this._length) {
        const buf = this._input.slice(0, this._length);
        arr.push(JSON.parse(buf.toString(CHAR)));
        this._input = this._input.length > this._length &&
                      this._input.slice(this._length) || null;
        this._length = null;
        if (this._input) {
          const cur = this._decodeMessage();
          cur.length && (arr = arr.concat(cur));
        }
      }
      return arr;
    }

    /**
     * read input
     * @param {string|Buffer} chunk - chunk
     * @param {Function} callback - callback
     * @throws {TypeError} - when callback is not a function
     * @returns {void}
     */
    read(chunk, callback) {
      const buf = (isString(chunk) || Buffer.isBuffer(chunk)) &&
                    Buffer.from(chunk);
      buf &&
        (this._input = this._input && Buffer.concat([this._input, buf]) || buf);
      if (callback) {
        const arr = this._decodeMessage();
        arr.length && arr.forEach(a => a && callback(a));
      }
    }
  }

  /* Output */
  class Output {
    /* encode message to buffer */
    constructor() {
      this._output;
    }

    /**
     * encode message
     * @throws {TypeError} - when message is not a JSON like object
     * @returns {Buffer} - buffered message
     */
    _encodeMessage() {
      let msg = JSON.stringify(this._output);
      if (!isString(msg)) {
        this._output = null;
        throw new TypeError(
          `${process.pid}: Expected a JSON like object, but got ${typeof msg}.`
        );
      }
      const buf = Buffer.from(msg);
      const len = Buffer.alloc(BYTE_LEN);
      IS_BE && len.writeUIntBE(buf.length, 0, BYTE_LEN) ||
      len.writeUIntLE(buf.length, 0, BYTE_LEN);
      msg = Buffer.concat([len, buf]);
      this._output = null;
      return Buffer.isBuffer(msg) && msg || null;
    }

    /**
     * write output
     * @param {Object} msg - message
     * @returns {?Buffer} - buffered message
     */
    write(msg) {
      this._output = msg;
      msg = this._encodeMessage();
      return Buffer.isBuffer(msg) && msg || null;
    }
  }

  module.exports = {
    Input, Output,
  };
}
<commit_msg>Refactor write output handling<commit_after>/**
 * native-message.js
 */
"use strict";
{
  /* api */
  const os = require("os");
  const process = require("process");

  /* constants */
  const BYTE_LEN = 4;
  const CHAR = "utf8";
  const IS_BE = os.endianness() === "BE";

  /**
   * is function
   * @param {*} o - object to check
   * @returns {boolean} - result
   */
  const isFunction = o => typeof o === "function";

  /**
   * is string
   * @param {*} o - object to check
   * @returns {boolean} - result
   */
  const isString = o => typeof o === "string" || o instanceof String;

  /* Input */
  class Input {
    /**
     * decode message from buffer
     */
    constructor() {
      this._input;
      this._length;
    }

    /**
     * decode message
     * @returns {Array} - message array
     */
    _decodeMessage() {
      let arr = [];
      !this._length && this._input && this._input.length >= BYTE_LEN && (
        this._length = IS_BE && this._input.readUIntBE(0, BYTE_LEN) ||
                       this._input.readUIntLE(0, BYTE_LEN),
        this._input = this._input.slice(BYTE_LEN)
      );
      if (this._length && this._input && this._input.length >= this._length) {
        const buf = this._input.slice(0, this._length);
        arr.push(JSON.parse(buf.toString(CHAR)));
        this._input = this._input.length > this._length &&
                      this._input.slice(this._length) || null;
        this._length = null;
        if (this._input) {
          const cur = this._decodeMessage();
          cur.length && (arr = arr.concat(cur));
        }
      }
      return arr;
    }

    /**
     * read input
     * @param {string|Buffer} chunk - chunk
     * @param {Function} callback - callback
     * @returns {void}
     */
    read(chunk, callback) {
      const buf = (isString(chunk) || Buffer.isBuffer(chunk)) &&
                    Buffer.from(chunk);
      buf &&
        (this._input = this._input && Buffer.concat([this._input, buf]) || buf);
      if (callback) {
        const arr = this._decodeMessage();
        arr.length && arr.forEach(a => a && callback(a));
      }
    }
  }

  /* Output */
  class Output {
    /* encode message to buffer */
    constructor() {
      this._output;
    }

    /**
     * encode message
     * @returns {?Buffer} - buffered message
     */
    _encodeMessage() {
      let msg = JSON.stringify(this._output);
      if (isString(msg)) {
        const buf = Buffer.from(msg);
        const len = Buffer.alloc(BYTE_LEN);
        IS_BE && len.writeUIntBE(buf.length, 0, BYTE_LEN) ||
        len.writeUIntLE(buf.length, 0, BYTE_LEN);
        msg = Buffer.concat([len, buf]);
      }
      this._output = null;
      return Buffer.isBuffer(msg) && msg || null;
    }

    /**
     * write output
     * @param {Object} msg - message
     * @returns {?Buffer} - buffered message
     */
    write(msg) {
      this._output = msg;
      msg = this._encodeMessage();
      return Buffer.isBuffer(msg) && msg || null;
    }
  }

  module.exports = {
    Input, Output,
  };
}
<|endoftext|>


<commit_before>var uuid = require('node-uuid');

/**
 * The PostmanCollection Constructor
 *
 * @constructor
 * @param data
 */
function PostmanCollection(data) {
  // postman basic data structure
  this.doc = {
    id: uuid.v1(),
    name: data.name || 'postman-collection-generator',
    description: data.description || 'no description available',
    order: [],
    folders: [],
    timestamp: data.timestamp || Date.now(),
    synced: data.synced || false,
    requests: []
  };
}
module.exports = PostmanCollection;

/**
 * addRequest
 */
PostmanCollection.prototype.addRequest = function(data) {
  var tmp = {
    id: uuid.v1(),
    name: data.name || 'unknown',
    description: data.description || 'not available',
    method: data.method || 'get',
    url: data.url || '',
    headers: data.headers || '',
    pathVariables: data.pathVariables || {},
    preRequestScript: data.preRequestScript || '',
    data: data.data || [],
    dataMode: data.dataMode || '',
    version: /*data.version ||*/ '2',
    tests: data.tests || '',
    responses: data.responses || [],
    synced: data.synced || false,
    collectionId: this.doc.id,
    time: data.time || Date.now()
  };

  this.doc.requests.push(tmp);

  this.doc.order.push(tmp.id);
};
<commit_msg>Added addFolders function, changes at addRequest and constructor<commit_after>var uuid = require('node-uuid');

var UNKNOWN = 'unknown';
var NOT_AVAILABLE = 'not available';

var METHOD = {
  GET: 'GET',
  POST: 'POST',
  PUT: 'PUT',
  DELETE: 'DELETE'
};

var DATA_MODE = {
  FORM_DATA: 'params',
  FORM_URLENCODED: 'urlencoded',
  RAW: 'raw',
  BINARY: 'binary'
};

/**
 * The PostmanCollection Constructor
 *
 * @constructor
 * @param data
 */
function PostmanCollection(data) {
  // check if data param exist
  var tmpData = {};
  if(data !== undefined) tmpData = data;

  // postman basic data structure
  this.doc = {
    id: uuid.v1(),
    name: tmpData.name || 'postman-collection',
    description: tmpData.description || NOT_AVAILABLE,
    order: [],
    folders: [],
    timestamp: tmpData.timestamp || Date.now(),
    synced: tmpData.synced || false,
    requests: []
  };

  // add folders and requests at init
  // TODO: unDRY this
  var self = this;
  if(tmpData.folders) {
    if(Array.isArray(tmpData.folders)) {
      tmpData.folders.forEach(function(folder) {
        self.addFolder(folder);
      });
    } else {
      console.error('folders must be an array');
    }
  }
  if(tmpData.requests) {
    if(Array.isArray(tmpData.requests)) {
      tmpData.requests.forEach(function(request) {
        self.addRequest(request);
      });
    } else {
      console.error('requests must be an array');
    }
  }
}
module.exports = PostmanCollection;

/**
 * Is method parameter valid?
 *
 * @param method
 * @returns {boolean}
 */
PostmanCollection.prototype.isMethodValid = function(method) {
  if(method) {
    switch(method) {
      case METHOD.GET:
      case METHOD.GET.toLowerCase():
      case METHOD.POST:
      case METHOD.POST.toLowerCase():
      case METHOD.PUT:
      case METHOD.PUT.toLowerCase():
      case METHOD.DELETE:
      case METHOD.DELETE.toLowerCase():
        return true;
      default:
        return false;
    }
  } else {
    return false;
  }
};

/**
 * Add a request item to the doc
 */
PostmanCollection.prototype.addRequest = function(data, folder) {
  var tmpData = data || {};

  // check method
  if(!this.isMethodValid(tmpData.method)) tmpData.method = METHOD.GET;

  var tmp = {
    id: uuid.v1(),
    name: tmpData.name || UNKNOWN,
    description: tmpData.description || NOT_AVAILABLE,
    descriptionFormat: 'html',
    method: tmpData.method,
    url: tmpData.url || '',
    headers: tmpData.headers || '',
    pathVariables: tmpData.pathVariables || {},
    preRequestScript: tmpData.preRequestScript || '',
    data: tmpData.data || [],
    dataMode: tmpData.dataMode || '',
    version: '2',
    tests: tmpData.tests || '',
    responses: tmpData.responses || [],
    synced: tmpData.synced || false,
    collectionId: this.doc.id,
    time: tmpData.time || Date.now()
  };

  // push to doc requests array
  this.doc.requests.push(tmp);

  // check if we want to add the request item to a folder
  if (folder) {
    this.addFolder({name: folder, order: [tmp.id]});
  }
  // push this id to the doc order array
  else {
    this.doc.order.push(tmp.id);
  }

  return tmp;
};

/**
 * Add a folder item to the doc
 */
PostmanCollection.prototype.addFolder = function(data) {
  var tmpData = data || {};

  var tmp = {
    id: uuid.v1(),
    name: tmpData.name || UNKNOWN,
    description: tmpData.description || NOT_AVAILABLE,
    order: tmpData.order || [],
    collection_id: this.doc.id,
    collection_name: this.doc.name
  };

  this.doc.folders.push(tmp);

  return tmp;
};
<|endoftext|>


<commit_before>var utils = require('./../../utils.js')

module.exports = function Recorder () {
  return function (module, controller) {
    var signalMethods = controller.getSignals()

    var currentSignal = null
    var currentSeek = 0
    var currentRecording = null
    var durationTimer = null
    var playbackTimers = []
    var duration = 0
    var started = null
    var ended = null
    var isPlaying = false
    var isRecording = false
    var isCatchingUp = false
    var startSeek = 0
    var catchup = null

    // Runs the signal synchronously
    var triggerSignal = function (signal) {
      var signalName = signal.name.split('.')
      var signalMethodPath = signalMethods
      while (signalName.length) {
        signalMethodPath = signalMethodPath[signalName.shift()]
      }
      currentSignal = signal
      signalMethodPath(signal.input, {
        isRecorded: !isCatchingUp,
        branches: isCatchingUp && signal.branches
      })
    }

    var services = {
      seek: function (seek) {
        startSeek = seek
        clearTimeout(durationTimer)
        playbackTimers.forEach(clearTimeout)

        controller.emit('seek', startSeek, currentRecording)

        // Optimize with FOR loop
        catchup = currentRecording.signals.filter(function (signal) {
          return signal.start - currentRecording.start < startSeek
        })
        isCatchingUp = true
        catchup.forEach(triggerSignal)
        isCatchingUp = false
      },

      getCurrentSignal: function () {
        return currentSignal
      },

      createTimer: function () {
        var update = function () {
          duration += 500
          controller.emit('duration', duration)
          if (duration < currentRecording.duration) {
            durationTimer = setTimeout(update, 500)
            controller.emit('change')
          }
        }
        durationTimer = setTimeout(update, 500)
      },

      // TODO: Do I need this? Not in use?
      resetState: function () {
        controller.emit('recorderReset', currentRecording)
      },

      play: function () {
        if (isPlaying || isRecording) {
          throw new Error('CEREBRAL Recorder - You can not play while already playing or recording')
        }

        this.createTimer()
        var signalsCount = currentRecording.signals.length
        var startIndex = catchup.length
        for (var x = startIndex; x < signalsCount; x++) {
          var signal = currentRecording.signals[x]
          var durationTarget = signal.start - currentRecording.start - startSeek
          playbackTimers.push(setTimeout(triggerSignal.bind(null, signal), durationTarget))
        }
        isPlaying = true
        started = Date.now()
      },

      record: function (options) {
        options = options || {}

        // If we are recording over the previous stuff, go back to start
        if (currentRecording) {
          this.resetState()
        }

        var paths = options.paths || [[]]
        var state = paths.map(function (path) {
          return {
            path: path,
            value: controller.get(path)
          }
        })

        currentRecording = {
          initialState: state,
          start: Date.now(),
          signals: []
        }

        isRecording = true
      },

      stop: function () {
        var wasPlaying = isPlaying
        clearTimeout(durationTimer)
        isPlaying = false
        isRecording = false

        if (wasPlaying) {
          return
        }

        currentRecording.end = Date.now()
        currentRecording.duration = currentRecording.end - currentRecording.start
      },

      pause: function pause () {
        ended = Date.now()
        currentSeek = ended - started
        clearTimeout(durationTimer)
        playbackTimers.forEach(clearTimeout)
        isPlaying = false
      },

      addSignal: function (signal) {
        currentRecording.signals.push(signal)
      },

      isRecording: function () {
        return isRecording
      },

      isPlaying: function () {
        return isPlaying
      },

      isCatchingUp: function () {
        return isCatchingUp
      },

      getRecording: function () {
        return currentRecording
      },

      getCurrentSeek: function () {
        return currentSeek
      },

      loadRecording: function (recording) {
        currentRecording = recording
      }
    }

    // uncomment after merge with cerebral-module-recorder
    // module.services(services)
    controller.getRecorder = function getRecorder () {
      console.warn('Cerebral: controller.getRecorder() is deprecated. Please upgrade recorder module.')
      return services
    }

    controller.on('signalStart', function (args) {
      var signal = args.signal

      if (isPlaying && !signal.options.isRecorded) {
        signal.preventSignalRun()
        if (utils.isDeveloping()) console.warn('Cerebral - Recording is replaying, ignored signal ' + signal.name)
      }

      if (isRecording) services.addSignal(args.signal)
    })
  }
}
<commit_msg>fix(recorder): use `signalTrigger` event to prevent signal run<commit_after>var utils = require('./../../utils.js')

module.exports = function Recorder () {
  return function (module, controller) {
    var signalMethods = controller.getSignals()

    var currentSignal = null
    var currentSeek = 0
    var currentRecording = null
    var durationTimer = null
    var playbackTimers = []
    var duration = 0
    var started = null
    var ended = null
    var isPlaying = false
    var isRecording = false
    var isCatchingUp = false
    var startSeek = 0
    var catchup = null

    // Runs the signal synchronously
    var triggerSignal = function (signal) {
      var signalName = signal.name.split('.')
      var signalMethodPath = signalMethods
      while (signalName.length) {
        signalMethodPath = signalMethodPath[signalName.shift()]
      }
      currentSignal = signal
      signalMethodPath(signal.input, {
        isRecorded: !isCatchingUp,
        branches: isCatchingUp && signal.branches
      })
    }

    var services = {
      seek: function (seek) {
        startSeek = seek
        clearTimeout(durationTimer)
        playbackTimers.forEach(clearTimeout)

        controller.emit('seek', startSeek, currentRecording)

        // Optimize with FOR loop
        catchup = currentRecording.signals.filter(function (signal) {
          return signal.start - currentRecording.start < startSeek
        })
        isCatchingUp = true
        catchup.forEach(triggerSignal)
        isCatchingUp = false
      },

      getCurrentSignal: function () {
        return currentSignal
      },

      createTimer: function () {
        var update = function () {
          duration += 500
          controller.emit('duration', duration)
          if (duration < currentRecording.duration) {
            durationTimer = setTimeout(update, 500)
            controller.emit('change')
          }
        }
        durationTimer = setTimeout(update, 500)
      },

      // TODO: Do I need this? Not in use?
      resetState: function () {
        controller.emit('recorderReset', currentRecording)
      },

      play: function () {
        if (isPlaying || isRecording) {
          throw new Error('CEREBRAL Recorder - You can not play while already playing or recording')
        }

        this.createTimer()
        var signalsCount = currentRecording.signals.length
        var startIndex = catchup.length
        for (var x = startIndex; x < signalsCount; x++) {
          var signal = currentRecording.signals[x]
          var durationTarget = signal.start - currentRecording.start - startSeek
          playbackTimers.push(setTimeout(triggerSignal.bind(null, signal), durationTarget))
        }
        isPlaying = true
        started = Date.now()
      },

      record: function (options) {
        options = options || {}

        // If we are recording over the previous stuff, go back to start
        if (currentRecording) {
          this.resetState()
        }

        var paths = options.paths || [[]]
        var state = paths.map(function (path) {
          return {
            path: path,
            value: controller.get(path)
          }
        })

        currentRecording = {
          initialState: state,
          start: Date.now(),
          signals: []
        }

        isRecording = true
      },

      stop: function () {
        var wasPlaying = isPlaying
        clearTimeout(durationTimer)
        isPlaying = false
        isRecording = false

        if (wasPlaying) {
          return
        }

        currentRecording.end = Date.now()
        currentRecording.duration = currentRecording.end - currentRecording.start
      },

      pause: function pause () {
        ended = Date.now()
        currentSeek = ended - started
        clearTimeout(durationTimer)
        playbackTimers.forEach(clearTimeout)
        isPlaying = false
      },

      addSignal: function (signal) {
        currentRecording.signals.push(signal)
      },

      isRecording: function () {
        return isRecording
      },

      isPlaying: function () {
        return isPlaying
      },

      isCatchingUp: function () {
        return isCatchingUp
      },

      getRecording: function () {
        return currentRecording
      },

      getCurrentSeek: function () {
        return currentSeek
      },

      loadRecording: function (recording) {
        currentRecording = recording
      }
    }

    // uncomment after merge with cerebral-module-recorder
    // module.services(services)
    controller.getRecorder = function getRecorder () {
      console.warn('Cerebral: controller.getRecorder() is deprecated. Please upgrade recorder module.')
      return services
    }

    controller.on('signalTrigger', function (args) {
      var signal = args.signal

      if (isPlaying && !signal.options.isRecorded) {
        signal.preventSignalRun()
        if (utils.isDeveloping()) console.warn('Cerebral - Recording is replaying, ignored signal ' + signal.name)
      }
    })

    controller.on('signalStart', function (args) {
      if (isRecording) services.addSignal(args.signal)
    })
  }
}
<|endoftext|>


<commit_before>const errors = require('@feathersjs/errors');
const { _ } = require('@feathersjs/commons');
const { sorter, select, AdapterService } = require('@feathersjs/adapter-commons');
const sift = require('sift').default;

const _select = (data, ...args) => {
  const base = select(...args);

  return base(JSON.parse(JSON.stringify(data)));
};

class Service extends AdapterService {
  constructor (options = {}) {
    super(_.extend({
      id: 'id',
      matcher: sift,
      sorter
    }, options));
    this.store = options.store || {};
    this._uId = options.startId || 0;
  }

  async getEntries (params = {}) {
    const { query } = this.filterQuery(params);

    return this._find(Object.assign({}, params, {
      paginate: false,
      query
    }));
  }

  async _find (params = {}) {
    const { query, filters, paginate } = this.filterQuery(params);
    let values = _.values(this.store).filter(this.options.matcher(query));
    const total = values.length;

    if (filters.$sort !== undefined) {
      values.sort(this.options.sorter(filters.$sort));
    }

    if (filters.$skip !== undefined) {
      values = values.slice(filters.$skip);
    }

    if (filters.$limit !== undefined) {
      values = values.slice(0, filters.$limit);
    }

    const result = {
      total,
      limit: filters.$limit,
      skip: filters.$skip || 0,
      data: values.map(value => _select(value, params))
    };

    if (!(paginate && paginate.default)) {
      return result.data;
    }

    return result;
  }

  async _get (id, params = {}) {
    if (id in this.store) {
      const { query } = this.filterQuery(params);
      const value = this.store[id];

      if (this.options.matcher(query)(value)) {
        return _select(value, params, this.id);
      }
    }

    throw new errors.NotFound(`No record found for id '${id}'`);
  }

  // Create without hooks and mixins that can be used internally
  async _create (data, params = {}) {
    if (Array.isArray(data)) {
      return Promise.all(data.map(current => this._create(current, params)));
    }

    const id = data[this.id] || this._uId++;
    const current = _.extend({}, data, { [this.id]: id });
    const result = (this.store[id] = current);

    return _select(result, params, this.id);
  }

  async _update (id, data, params = {}) {
    const oldEntry = await this._get(id);
    // We don't want our id to change type if it can be coerced
    const oldId = oldEntry[this.id];

    id = oldId == id ? oldId : id; // eslint-disable-line

    this.store[id] = _.extend({}, data, { [this.id]: id });

    return this._get(id, params);
  }

  async _patch (id, data, params = {}) {
    const patchEntry = entry => {
      const currentId = entry[this.id];

      this.store[currentId] = _.extend(this.store[currentId], _.omit(data, this.id));

      return _select(this.store[currentId], params, this.id);
    };

    if (id === null) {
      const entries = await this.getEntries(params);

      return entries.map(patchEntry);
    }

    return patchEntry(await this._get(id, params)); // Will throw an error if not found
  }

  // Remove without hooks and mixins that can be used internally
  async _remove (id, params = {}) {
    if (id === null) {
      const entries = await this.getEntries(params);

      return Promise.all(entries.map(
        current => this._remove(current[this.id], params))
      );
    }

    const entry = await this._get(id, params);

    delete this.store[id];

    return entry;
  }
}

module.exports = options => {
  return new Service(options);
};

module.exports.Service = Service;
<commit_msg>Make rebuild valid<commit_after>const errors = require('@feathersjs/errors');
const { _ } = require('@feathersjs/commons');
const { sorter, select, AdapterService } = require('@feathersjs/adapter-commons');
const sift = require('sift').default;

const _select = (data, ...args) => {
  const base = select(...args);

  return base(JSON.parse(JSON.stringify(data)));
};

class Service extends AdapterService {
  constructor (options = {}) {
    super(_.extend({
      id: 'id',
      matcher: sift,
      sorter
    }, options));
    this._uId = options.startId || 0;
    this.store = options.store || {};
  }

  async getEntries (params = {}) {
    const { query } = this.filterQuery(params);

    return this._find(Object.assign({}, params, {
      paginate: false,
      query
    }));
  }

  async _find (params = {}) {
    const { query, filters, paginate } = this.filterQuery(params);
    let values = _.values(this.store).filter(this.options.matcher(query));
    const total = values.length;

    if (filters.$sort !== undefined) {
      values.sort(this.options.sorter(filters.$sort));
    }

    if (filters.$skip !== undefined) {
      values = values.slice(filters.$skip);
    }

    if (filters.$limit !== undefined) {
      values = values.slice(0, filters.$limit);
    }

    const result = {
      total,
      limit: filters.$limit,
      skip: filters.$skip || 0,
      data: values.map(value => _select(value, params))
    };

    if (!(paginate && paginate.default)) {
      return result.data;
    }

    return result;
  }

  async _get (id, params = {}) {
    if (id in this.store) {
      const { query } = this.filterQuery(params);
      const value = this.store[id];

      if (this.options.matcher(query)(value)) {
        return _select(value, params, this.id);
      }
    }

    throw new errors.NotFound(`No record found for id '${id}'`);
  }

  // Create without hooks and mixins that can be used internally
  async _create (data, params = {}) {
    if (Array.isArray(data)) {
      return Promise.all(data.map(current => this._create(current, params)));
    }

    const id = data[this.id] || this._uId++;
    const current = _.extend({}, data, { [this.id]: id });
    const result = (this.store[id] = current);

    return _select(result, params, this.id);
  }

  async _update (id, data, params = {}) {
    const oldEntry = await this._get(id);
    // We don't want our id to change type if it can be coerced
    const oldId = oldEntry[this.id];

    id = oldId == id ? oldId : id; // eslint-disable-line

    this.store[id] = _.extend({}, data, { [this.id]: id });

    return this._get(id, params);
  }

  async _patch (id, data, params = {}) {
    const patchEntry = entry => {
      const currentId = entry[this.id];

      this.store[currentId] = _.extend(this.store[currentId], _.omit(data, this.id));

      return _select(this.store[currentId], params, this.id);
    };

    if (id === null) {
      const entries = await this.getEntries(params);

      return entries.map(patchEntry);
    }

    return patchEntry(await this._get(id, params)); // Will throw an error if not found
  }

  // Remove without hooks and mixins that can be used internally
  async _remove (id, params = {}) {
    if (id === null) {
      const entries = await this.getEntries(params);

      return Promise.all(entries.map(
        current => this._remove(current[this.id], params))
      );
    }

    const entry = await this._get(id, params);

    delete this.store[id];

    return entry;
  }
}

module.exports = options => {
  return new Service(options);
};

module.exports.Service = Service;
<|endoftext|>