all files / raven-node/lib/ utils.js

87.61% Statements 99/113
79.17% Branches 57/72
92.31% Functions 12/13
88.79% Lines 95/107
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208                      23×                                 25× 25× 25× 25× 25× 25×     88×     84× 84×               84×     84×     83× 83× 83× 83× 83×         25× 24×       30×   30×         304× 304×                     307×     307× 307× 307× 307×   207×       100× 100×   99×     26×     26×     25×     25×       22×     22×   22× 304×                 304×     304×     304× 70× 70× 70×     234×           234× 234× 234× 234× 234×   234× 234×     234× 234× 234×            
'use strict';
 
var fs = require('fs');
var url = require('url');
var transports = require('./transports');
var path = require('path');
var lsmod = require('lsmod');
var stacktrace = require('stack-trace');
 
var ravenVersion = require('../package.json').version;
 
var protocolMap = {
  http: 80,
  https: 443
};
 
var consoleAlerts = {};
 
module.exports.disableConsoleAlerts = function disableConsoleAlerts() {
  consoleAlerts = false;
};
 
module.exports.consoleAlert = function consoleAlert(msg) {
  Iif (consoleAlerts && !(msg in consoleAlerts)) {
    consoleAlerts[msg] = true;
    console.log('raven@' + ravenVersion + ' alert: ' + msg);
  }
};
 
module.exports.extend = Object.assign || function (target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i];
    for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key];
      }
    }
  }
  return target;
};
 
module.exports.getAuthHeader = function getAuthHeader(timestamp, apiKey, apiSecret) {
  var header = ['Sentry sentry_version=5'];
  header.push('sentry_timestamp=' + timestamp);
  header.push('sentry_client=raven-node/' + ravenVersion);
  header.push('sentry_key=' + apiKey);
  header.push('sentry_secret=' + apiSecret);
  return header.join(', ');
};
 
module.exports.parseDSN = function parseDSN(dsn) {
  if (!dsn) {
    // Let a falsey value return false explicitly
    return false;
  }
  try {
    var parsed = url.parse(dsn),
        response = {
          protocol: parsed.protocol.slice(0, -1),
          public_key: parsed.auth.split(':')[0],
          private_key: parsed.auth.split(':')[1],
          host: parsed.host.split(':')[0]
        };
 
    if (~response.protocol.indexOf('+')) {
      response.protocol = response.protocol.split('+')[1];
    }
 
    if (!transports.hasOwnProperty(response.protocol)) {
      throw new Error('Invalid transport');
    }
 
    var index = parsed.pathname.lastIndexOf('/');
    response.path = parsed.pathname.substr(0, index + 1);
    response.project_id = parsed.pathname.substr(index + 1);
    response.port = ~~parsed.port || protocolMap[response.protocol] || 443;
    return response;
  } catch (e) {
    throw new Error('Invalid Sentry DSN: ' + dsn);
  }
};
 
module.exports.getCulprit = function getCulprit(frame) {
  if (frame.module || frame.function) {
    return (frame.module || '?') + ' at ' + (frame.function || '?');
  }
  return '<unknown>';
};
 
var moduleCache;
module.exports.getModules = function getModules() {
  if (!moduleCache) {
    moduleCache = lsmod();
  }
  return moduleCache;
};
 
 
var LINES_OF_CONTEXT = 7;
 
function getFunction(line) {
  try {
    return line.getFunctionName() ||
      line.getTypeName() + '.' + (line.getMethodName() || '<anonymous>');
  } catch (e) {
    // This seems to happen sometimes when using 'use strict',
    // stemming from `getTypeName`.
    // [TypeError: Cannot read property 'constructor' of undefined]
    return '<anonymous>';
  }
}
 
var mainModule = (require.main && require.main.filename && path.dirname(require.main.filename) || process.cwd()) + '/';
 
function getModule(filename, base) {
  if (!base) base = mainModule;
 
  // It's specifically a module
  var file = path.basename(filename, '.js');
  filename = path.dirname(filename);
  var n = filename.lastIndexOf('/node_modules/');
  if (n > -1) {
    // /node_modules/ is 14 chars
    return filename.substr(n + 14).replace(/\//g, '.') + ':' + file;
  }
  // Let's see if it's a part of the main module
  // To be a part of main module, it has to share the same base
  n = (filename + '/').lastIndexOf(base, 0);
  if (n === 0) {
    var module = filename.substr(base.length).replace(/\//g, '.');
    Eif (module) module += ':';
    module += file;
    return module;
  }
  return file;
}
 
function parseStack(err, cb) {
  var frames = [],
      cache = {};
 
  if (!err) {
    return cb(frames);
  }
 
  var stack = stacktrace.parse(err);
 
  // check to make sure that the stack is what we need it to be.
  if (!stack || !Array.isArray(stack) || !stack.length || !stack[0].getFileName) {
    // lol, stack is fucked
    return cb(frames);
  }
 
  var callbacks = stack.length;
 
  // Sentry requires the stack trace to be from oldest to newest
  stack.reverse();
 
  return stack.forEach(function (line, index) {
    var frame = {
          filename: line.getFileName() || '',
          lineno: line.getLineNumber(),
          colno: line.getColumnNumber(),
          'function': getFunction(line),
        },
        isInternal = line.isNative() || frame.filename[0] !== '/' && frame.filename[0] !== '.';
 
    // in_app is all that's not an internal Node function or a module within node_modules
    frame.in_app = !isInternal && !~frame.filename.indexOf('node_modules/');
 
    // Extract a module name based on the filename
    Eif (frame.filename) frame.module = getModule(frame.filename);
 
    // internal Node files are not full path names. Ignore them.
    if (isInternal) {
      frames[index] = frame;
      Iif (--callbacks === 0) cb(frames);
      return;
    }
 
    Iif (frame.filename in cache) {
      parseLines(cache[frame.filename]);
      if (--callbacks === 0) cb(frames);
      return;
    }
 
    fs.readFile(frame.filename, function (_err, file) {
      Eif (!_err) {
        file = file.toString().split('\n');
        cache[frame.filename] = file;
        parseLines(file, frame);
      }
      frames[index] = frame;
      if (--callbacks === 0) cb(frames);
    });
 
    function parseLines(lines) {
      frame.pre_context = lines.slice(Math.max(0, frame.lineno - (LINES_OF_CONTEXT + 1)), frame.lineno - 1);
      frame.context_line = lines[frame.lineno - 1];
      frame.post_context = lines.slice(frame.lineno, frame.lineno + LINES_OF_CONTEXT);
    }
  });
}
 
// expose basically for testing because I don't know what I'm doing
module.exports.parseStack = parseStack;
module.exports.getModule = getModule;