From b84f861cc1d8632934704ab1202e528ccac50c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Og=C3=B3rek?= Date: Mon, 27 Nov 2017 12:01:19 +0100 Subject: [PATCH 1/3] feat(processing): Enable Node.js Source Maps --- src/sentry/interfaces/stacktrace.py | 2 +- src/sentry/lang/javascript/plugin.py | 9 +- src/sentry/lang/javascript/processor.py | 28 ++- src/sentry/models/featureadoption.py | 2 +- .../models/organizationonboardingtask.py | 2 +- src/sentry/utils/javascript.py | 2 +- .../lang/javascript/fixtures/node_app.min.js | 2 + .../javascript/fixtures/node_app.min.js.map | 1 + tests/sentry/lang/javascript/test_plugin.py | 170 ++++++++++++++++++ 9 files changed, 204 insertions(+), 14 deletions(-) create mode 100644 tests/sentry/lang/javascript/fixtures/node_app.min.js create mode 100644 tests/sentry/lang/javascript/fixtures/node_app.min.js.map diff --git a/src/sentry/interfaces/stacktrace.py b/src/sentry/interfaces/stacktrace.py index 7eea62e02bec80..ff54bd1a1e4f6c 100644 --- a/src/sentry/interfaces/stacktrace.py +++ b/src/sentry/interfaces/stacktrace.py @@ -537,7 +537,7 @@ def get_culprit_string(self, platform=None): fileloc = self.module or self.filename if not fileloc: return '' - elif platform == 'javascript': + elif platform in ('javascript', 'node'): # function and fileloc might be unicode here, so let it coerce # to a unicode string if needed. return '%s(%s)' % (self.function or '?', fileloc) diff --git a/src/sentry/lang/javascript/plugin.py b/src/sentry/lang/javascript/plugin.py index 122c0ce4610961..0702b4b6cb714b 100644 --- a/src/sentry/lang/javascript/plugin.py +++ b/src/sentry/lang/javascript/plugin.py @@ -12,7 +12,8 @@ def preprocess_event(data): rewrite_exception(data) fix_culprit(data) - inject_device_data(data) + if data.get('platform') == 'javascript': + inject_device_data(data) generate_modules(data) return data @@ -23,7 +24,7 @@ def generate_modules(data): for info in find_stacktraces_in_data(data): for frame in info.stacktrace['frames']: platform = frame.get('platform') or data['platform'] - if platform != 'javascript' or frame.get('module'): + if platform not in ('javascript', 'node') or frame.get('module'): continue abs_path = frame.get('abs_path') if abs_path and abs_path.startswith(('http:', 'https:', 'webpack:', 'app:')): @@ -129,10 +130,10 @@ def can_configure_for_project(self, project, **kwargs): def get_event_preprocessors(self, data, **kwargs): # XXX: rewrite_exception we probably also want if the event # platform is something else? unsure - if data.get('platform') == 'javascript': + if data.get('platform') in ('javascript', 'node'): return [preprocess_event] return [] def get_stacktrace_processors(self, data, stacktrace_infos, platforms, **kwargs): - if 'javascript' in platforms: + if 'javascript' in platforms or 'node' in platforms: return [JavaScriptStacktraceProcessor] diff --git a/src/sentry/lang/javascript/processor.py b/src/sentry/lang/javascript/processor.py index 705d2304162364..0cd5c719b73e4f 100644 --- a/src/sentry/lang/javascript/processor.py +++ b/src/sentry/lang/javascript/processor.py @@ -56,7 +56,7 @@ class ZeroReturnError(Exception): VERSION_RE = re.compile(r'^[a-f0-9]{32}|[a-f0-9]{40}$', re.I) NODE_MODULES_RE = re.compile(r'\bnode_modules/') SOURCE_MAPPING_URL_RE = re.compile(r'\/\/# sourceMappingURL=(.*)$') -# the maximum number of remote resources (i.e. sourc eifles) that should be +# the maximum number of remote resources (i.e. source files) that should be # fetched MAX_RESOURCE_FETCHES = 100 @@ -495,7 +495,7 @@ def preprocess_step(self, processing_task): def handles_frame(self, frame, stacktrace_info): platform = frame.get('platform') or self.data.get('platform') - return (settings.SENTRY_SCRAPE_JAVASCRIPT_CONTEXT and platform == 'javascript') + return (settings.SENTRY_SCRAPE_JAVASCRIPT_CONTEXT and platform in ('javascript', 'node')) def preprocess_frame(self, processable_frame): # Stores the resolved token. This is used to cross refer to other @@ -517,6 +517,13 @@ def process_frame(self, processable_frame, processing_task): if not frame.get('abs_path') or not frame.get('lineno'): return + # can't fetch if this is internal node module as well + # therefore we only process user-land frames (starting with /) + # or those created by bundle/webpack internals + if self.data.get('platform') == 'node' and \ + not frame.get('abs_path').startswith(('/', 'app:', 'webpack:')): + return + errors = cache.get_errors(frame['abs_path']) if errors: all_errors.extend(errors) @@ -588,7 +595,7 @@ def process_frame(self, processable_frame, processing_task): ) source = self.get_sourceview(abs_path) - if not source: + if source is None: errors = cache.get_errors(abs_path) if errors: all_errors.extend(errors) @@ -639,9 +646,14 @@ def process_frame(self, processable_frame, processing_task): else: filename = filename.split('webpack:///', 1)[-1] - # As noted above, '~/' means they're coming from node_modules, - # so these are not app dependencies - if filename.startswith('~/'): + # As noted above: + # * [js/node] '~/' means they're coming from node_modules, so these are not app dependencies + # * [node] sames goes for `./node_modules/`, which is used when bundling node apps + # * [node] and webpack, which includes it's own code to bootstrap all modules and its internals + # eg. webpack:///webpack/bootstrap, webpack:///external + if filename.startswith('~/') or \ + filename.startswith('./node_modules/') or \ + not filename.startswith('./'): in_app = False # And conversely, local dependencies start with './' elif filename.startswith('./'): @@ -791,6 +803,10 @@ def populate_source_cache(self, frames): # a fetch error that may be confusing. if f['abs_path'] == '': continue + # we cannot fetch any other files than those uploaded by user + if self.data.get('platform') == 'node' and \ + not f.get('abs_path').startswith('app:'): + continue pending_file_list.add(f['abs_path']) for idx, filename in enumerate(pending_file_list): diff --git a/src/sentry/models/featureadoption.py b/src/sentry/models/featureadoption.py index 103adc18c10d75..46cdcf9b3fe8a7 100644 --- a/src/sentry/models/featureadoption.py +++ b/src/sentry/models/featureadoption.py @@ -50,7 +50,7 @@ ) manager.add(43, "user_tracking", "User Tracking", "code", prerequisite=["first_event"]) manager.add(44, "custom_tags", "Custom Tags", "code", prerequisite=["first_event"]) -manager.add(45, "source_maps", "Source Maps", "code", prerequisite=["first_event", "javascript"]) +manager.add(45, "source_maps", "Source Maps", "code", prerequisite=["first_event", ("javascript", "node")]) manager.add(46, "user_feedback", "User Feedback", "code", prerequisite=["user_tracking"]) # manager.add(47, "api", "API", "code", prerequisite=["first_event"]) # # Challenging to determine what organization (i.e. api/0/organizations/) diff --git a/src/sentry/models/organizationonboardingtask.py b/src/sentry/models/organizationonboardingtask.py index 660dc4f1205473..432615d26ecf6f 100644 --- a/src/sentry/models/organizationonboardingtask.py +++ b/src/sentry/models/organizationonboardingtask.py @@ -26,7 +26,7 @@ class OnboardingTask(object): SECOND_PLATFORM = 4 # dependent on FIRST_EVENT. USER_CONTEXT = 5 # dependent on FIRST_EVENT RELEASE_TRACKING = 6 # dependent on FIRST_EVENT - SOURCEMAPS = 7 # dependent on RELEASE_TRACKING and one of the platforms being javascript + SOURCEMAPS = 7 # dependent on RELEASE_TRACKING and one of the platforms being javascript or node USER_REPORTS = 8 # Only for web frameworks ISSUE_TRACKER = 9 NOTIFICATION_SERVICE = 10 diff --git a/src/sentry/utils/javascript.py b/src/sentry/utils/javascript.py index 249626acccc743..e38c8d030beb97 100644 --- a/src/sentry/utils/javascript.py +++ b/src/sentry/utils/javascript.py @@ -9,7 +9,7 @@ def has_sourcemap(event): - if event.platform != 'javascript': + if event.platform not in ('javascript', 'node'): return False data = event.data diff --git a/tests/sentry/lang/javascript/fixtures/node_app.min.js b/tests/sentry/lang/javascript/fixtures/node_app.min.js new file mode 100644 index 00000000000000..636a146b012925 --- /dev/null +++ b/tests/sentry/lang/javascript/fixtures/node_app.min.js @@ -0,0 +1,2 @@ +!function(n){function t(e){if(r[e])return r[e].exports;var o=r[e]={i:e,l:!1,exports:{}};return n[e].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};t.m=n,t.c=r,t.d=function(n,r,e){t.o(n,r)||Object.defineProperty(n,r,{configurable:!1,enumerable:!0,get:e})},t.n=function(n){var r=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(r,"a",r),r},t.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},t.p="",t(t.s=0)}([function(n,t,r){var e=r(1);setTimeout(function(){e()},1e3)},function(n,t,r){function e(){o()}var o=r(2);n.exports=e},function(n,t,r){var e=r(3);n.exports=function(){return e.join("foo","bar")}},function(n,t){n.exports=require("path")}]); +//# sourceMappingURL=app.bundle.js.map \ No newline at end of file diff --git a/tests/sentry/lang/javascript/fixtures/node_app.min.js.map b/tests/sentry/lang/javascript/fixtures/node_app.min.js.map new file mode 100644 index 00000000000000..15a94ef9d31f01 --- /dev/null +++ b/tests/sentry/lang/javascript/fixtures/node_app.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///app.bundle.js","webpack:///webpack/bootstrap b4563923c1a5a5292138","webpack:///./index.js","webpack:///./src/foo.js","webpack:///./src/bar.js","webpack:///external \"path\""],"names":["modules","__webpack_require__","moduleId","installedModules","exports","module","i","l","call","m","c","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","foo","setTimeout","bar","path","join","require"],"mappings":"CAAS,SAAUA,GCInB,QAAAC,GAAAC,GAGA,GAAAC,EAAAD,GACA,MAAAC,GAAAD,GAAAE,OAGA,IAAAC,GAAAF,EAAAD,IACAI,EAAAJ,EACAK,GAAA,EACAH,WAUA,OANAJ,GAAAE,GAAAM,KAAAH,EAAAD,QAAAC,IAAAD,QAAAH,GAGAI,EAAAE,GAAA,EAGAF,EAAAD,QAvBA,GAAAD,KA4BAF,GAAAQ,EAAAT,EAGAC,EAAAS,EAAAP,EAGAF,EAAAU,EAAA,SAAAP,EAAAQ,EAAAC,GACAZ,EAAAa,EAAAV,EAAAQ,IACAG,OAAAC,eAAAZ,EAAAQ,GACAK,cAAA,EACAC,YAAA,EACAC,IAAAN,KAMAZ,EAAAmB,EAAA,SAAAf,GACA,GAAAQ,GAAAR,KAAAgB,WACA,WAA2B,MAAAhB,GAAA,SAC3B,WAAiC,MAAAA,GAEjC,OADAJ,GAAAU,EAAAE,EAAA,IAAAA,GACAA,GAIAZ,EAAAa,EAAA,SAAAQ,EAAAC,GAAsD,MAAAR,QAAAS,UAAAC,eAAAjB,KAAAc,EAAAC,IAGtDtB,EAAAyB,EAAA,GAGAzB,IAAA0B,EAAA,KDMM,SAAUtB,EAAQD,EAASH,GEnEjC,GAAA2B,GAAA3B,EAAA,EAEA4B,YAAA,WACAD,KACC,MF2EK,SAAUvB,EAAQD,EAASH,GG7EjC,QAAA2B,KACAE,IAHA,GAAAA,GAAA7B,EAAA,EAMAI,GAAAD,QAAAwB,GHsFM,SAAUvB,EAAQD,EAASH,GI5FjC,GAAA8B,GAAA9B,EAAA,EAEAI,GAAAD,QAAA,WACA,MAAA2B,GAAAC,KAAA,eJoGM,SAAU3B,EAAQD,GKvGxBC,EAAAD,QAAA6B,QAAA","file":"app.bundle.js","sourcesContent":["/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar foo = __webpack_require__(1);\n\nsetTimeout(function () {\n foo();\n}, 1000);\n\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar bar = __webpack_require__(2);\n\nfunction foo() {\n bar();\n}\n\nmodule.exports = foo;\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar path = __webpack_require__(3);\n\nmodule.exports = function bar() {\n return path.join('foo', 'bar');\n}\n\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports) {\n\nmodule.exports = require(\"path\");\n\n/***/ })\n/******/ ]);\n\n\n// WEBPACK FOOTER //\n// app.bundle.js"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap b4563923c1a5a5292138","var foo = require('./src/foo.js');\n\nsetTimeout(function () {\n foo();\n}, 1000);\n\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./index.js\n// module id = 0\n// module chunks = 0","var bar = require('./bar.js');\n\nfunction foo() {\n bar();\n}\n\nmodule.exports = foo;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/foo.js\n// module id = 1\n// module chunks = 0","var path = require('path');\n\nmodule.exports = function bar() {\n return path.join('foo', 'bar');\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/bar.js\n// module id = 2\n// module chunks = 0","module.exports = require(\"path\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"path\"\n// module id = 3\n// module chunks = 0"],"sourceRoot":""} \ No newline at end of file diff --git a/tests/sentry/lang/javascript/test_plugin.py b/tests/sentry/lang/javascript/test_plugin.py index 238a3db68ce54e..92a9c610ad6747 100644 --- a/tests/sentry/lang/javascript/test_plugin.py +++ b/tests/sentry/lang/javascript/test_plugin.py @@ -1078,3 +1078,173 @@ def test_html_response_for_js(self): 'type': 'js_invalid_content' } ] + + @patch('sentry.lang.javascript.processor.fetch_file') + def test_foo(self, mock_fetch_file): + data = { + 'message': 'hello', + 'platform': 'node', + 'sentry.interfaces.Exception': { + 'values': [ + { + 'type': 'Error', + 'stacktrace': { + 'frames': [ + { + 'abs_path': 'node_bootstrap.js', + 'filename': 'node_bootstrap.js', + 'lineno': 1, + 'colno': 38, + }, + { + 'abs_path': 'timers.js', + 'filename': 'timers.js', + 'lineno': 1, + 'colno': 39, + }, + { + 'abs_path': 'webpack:///internal', + 'filename': 'internal', + 'lineno': 1, + 'colno': 43, + }, + { + 'abs_path': 'webpack:///~/some_dep/file.js', + 'filename': 'file.js', + 'lineno': 1, + 'colno': 41, + }, + { + 'abs_path': 'webpack:///./node_modules/file.js', + 'filename': 'file.js', + 'lineno': 1, + 'colno': 42, + }, + { + 'abs_path': 'app:///file.js', + 'filename': 'file.js', + 'lineno': 1, + 'colno': 40, + }, + ], + }, + } + ], + } + } + + mock_fetch_file.return_value.body = '\n'.join('hello world') + mock_fetch_file.return_value.encoding = None + + resp = self._postWithHeader(data) + assert resp.status_code, 200 + + assert mock_fetch_file.call_count == 3 + + args, kwargs = mock_fetch_file.call_args_list[0] + assert args[0] == 'app:///file.js' + args, kwargs = mock_fetch_file.call_args_list[1] + assert args[0] == 'webpack:///~/some_dep/file.js' + args, kwargs = mock_fetch_file.call_args_list[2] + assert args[0] == 'webpack:///./node_modules/file.js' + args, kwargs = mock_fetch_file.call_args_list[3] + assert args[0] == 'webpack:///internal', + + event = Event.objects.get() + + exception = event.interfaces['sentry.interfaces.Exception'] + frame_list = exception.values[0].stacktrace.frames + + assert not frame_list[0].in_app + assert not frame_list[1].in_app + assert not frame_list[2].in_app + assert not frame_list[3].in_app + assert not frame_list[4].in_app + assert frame_list[5].in_app + + @responses.activate + def test_bar(self): + responses.add( + responses.GET, + 'http://example.com/node_app.min.js', + body=load_fixture('node_app.min.js'), + content_type='application/javascript; charset=utf-8' + ) + responses.add( + responses.GET, + 'http://example.com/node_app.min.js.map', + body=load_fixture('node_app.min.js.map'), + content_type='application/javascript; charset=utf-8' + ) + + data = { + 'message': 'hello', + 'platform': 'node', + 'sentry.interfaces.Exception': { + 'values': [ + { + 'type': 'Error', + 'stacktrace': { + 'frames': [ + { + 'abs_path': 'node_bootstrap.js', + 'filename': 'node_bootstrap.js', + 'lineno': 1, + 'colno': 38, + }, + { + 'abs_path': 'timers.js', + 'filename': 'timers.js', + 'lineno': 1, + 'colno': 39, + }, + { + 'abs_path': 'webpack:///internal', + 'filename': 'internal', + 'lineno': 1, + 'colno': 43, + }, + { + 'abs_path': 'webpack:///~/some_dep/file.js', + 'filename': 'file.js', + 'lineno': 1, + 'colno': 41, + }, + { + 'abs_path': 'webpack:///./node_modules/file.js', + 'filename': 'file.js', + 'lineno': 1, + 'colno': 42, + }, + { + 'abs_path': 'http://example.com/node_app.min.js', + 'filename': 'node_app.min.js', + 'lineno': 1, + 'colno': 40, + }, + ], + }, + } + ], + } + } + + resp = self._postWithHeader(data) + assert resp.status_code, 200 + + event = Event.objects.get() + exception = event.interfaces['sentry.interfaces.Exception'] + frame_list = exception.values[0].stacktrace.frames + raw_frame_list = exception.values[0].raw_stacktrace.frames + + assert not frame_list[0].in_app + assert not frame_list[1].in_app + assert not frame_list[2].in_app + assert not frame_list[3].in_app + assert not frame_list[4].in_app + assert frame_list[5].in_app + + # Since we couldn't expand source for the 1st and 2nd frame, both + # its raw and original form should be identical + assert raw_frame_list[0] == frame_list[0] + assert raw_frame_list[1] == frame_list[1] From d1f184c2dc15b365f0f4ba32b2e6e9f203bfc962 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 11 Dec 2017 12:14:09 +0100 Subject: [PATCH 2/3] test(javascript): Properly assert that we are not fetching in JS --- tests/sentry/lang/javascript/test_plugin.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/tests/sentry/lang/javascript/test_plugin.py b/tests/sentry/lang/javascript/test_plugin.py index 92a9c610ad6747..3a5f6dd70c8428 100644 --- a/tests/sentry/lang/javascript/test_plugin.py +++ b/tests/sentry/lang/javascript/test_plugin.py @@ -1148,7 +1148,7 @@ def test_foo(self, mock_fetch_file): args, kwargs = mock_fetch_file.call_args_list[2] assert args[0] == 'webpack:///./node_modules/file.js' args, kwargs = mock_fetch_file.call_args_list[3] - assert args[0] == 'webpack:///internal', + assert args[0] == 'webpack:///internal' event = Event.objects.get() @@ -1163,7 +1163,7 @@ def test_foo(self, mock_fetch_file): assert frame_list[5].in_app @responses.activate - def test_bar(self): + def test_no_fetch_from_http(self): responses.add( responses.GET, 'http://example.com/node_app.min.js', @@ -1235,16 +1235,10 @@ def test_bar(self): event = Event.objects.get() exception = event.interfaces['sentry.interfaces.Exception'] frame_list = exception.values[0].stacktrace.frames - raw_frame_list = exception.values[0].raw_stacktrace.frames - assert not frame_list[0].in_app - assert not frame_list[1].in_app - assert not frame_list[2].in_app - assert not frame_list[3].in_app - assert not frame_list[4].in_app - assert frame_list[5].in_app + # This one should not process, so this one should be none. + assert exception.values[0].raw_stacktrace is None - # Since we couldn't expand source for the 1st and 2nd frame, both - # its raw and original form should be identical - assert raw_frame_list[0] == frame_list[0] - assert raw_frame_list[1] == frame_list[1] + # None of the in app should update + for x in range(6): + assert not frame_list[x].in_app From d4f7e968cc4bfe0731d2a941ba26704f3d6afc80 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 14 Dec 2017 12:42:15 +0100 Subject: [PATCH 3/3] test(sourcemaps): Fixed tests for node sourcemaps --- .../lang/javascript/fixtures/dist.bundle.js | 2327 +++++++++++++++++ .../javascript/fixtures/dist.bundle.js.map | 1 + tests/sentry/lang/javascript/test_plugin.py | 138 +- 3 files changed, 2420 insertions(+), 46 deletions(-) create mode 100644 tests/sentry/lang/javascript/fixtures/dist.bundle.js create mode 100644 tests/sentry/lang/javascript/fixtures/dist.bundle.js.map diff --git a/tests/sentry/lang/javascript/fixtures/dist.bundle.js b/tests/sentry/lang/javascript/fixtures/dist.bundle.js new file mode 100644 index 00000000000000..47e4870662b68f --- /dev/null +++ b/tests/sentry/lang/javascript/fixtures/dist.bundle.js @@ -0,0 +1,2327 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 15); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var fs = __webpack_require__(8); +var url = __webpack_require__(7); +var transports = __webpack_require__(3); +var path = __webpack_require__(2); +var lsmod = __webpack_require__(21); +var stacktrace = __webpack_require__(22); + +var ravenVersion = __webpack_require__(11).version; + +var protocolMap = { + http: 80, + https: 443 +}; + +var consoleAlerts = {}; + +module.exports.disableConsoleAlerts = function disableConsoleAlerts() { + consoleAlerts = false; +}; + +module.exports.consoleAlert = function consoleAlert(msg) { + if (consoleAlerts) { + console.log('raven@' + ravenVersion + ' alert: ' + msg); + } +}; + +module.exports.consoleAlertOnce = function consoleAlertOnce(msg) { + if (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); + if (apiSecret) 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], + host: parsed.host.split(':')[0] + }; + + if (parsed.auth.split(':')[1]) { + response.private_key = parsed.auth.split(':')[1]; + } + + 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 ''; +}; + +var moduleCache; +module.exports.getModules = function getModules() { + if (!moduleCache) { + moduleCache = lsmod(); + } + return moduleCache; +}; + +module.exports.fill = function(obj, name, replacement, track) { + var orig = obj[name]; + obj[name] = replacement(orig); + if (track) { + track.push([obj, name, orig]); + } +}; + +var LINES_OF_CONTEXT = 7; + +function getFunction(line) { + try { + return ( + line.getFunctionName() || + line.getTypeName() + '.' + (line.getMethodName() || '') + ); + } catch (e) { + // This seems to happen sometimes when using 'use strict', + // stemming from `getTypeName`. + // [TypeError: Cannot read property 'constructor' of undefined] + return ''; + } +} + +var mainModule = + ((__webpack_require__.c[__webpack_require__.s] && __webpack_require__.c[__webpack_require__.s].filename && path.dirname(__webpack_require__.c[__webpack_require__.s].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, '.'); + if (module) module += ':'; + module += file; + return module; + } + return file; +} + +function readSourceFiles(filenames, cb) { + // we're relying on filenames being de-duped already + if (filenames.length === 0) return setTimeout(cb, 0, {}); + + var sourceFiles = {}; + var numFilesToRead = filenames.length; + return filenames.forEach(function(filename) { + fs.readFile(filename, function(readErr, file) { + if (!readErr) sourceFiles[filename] = file.toString().split('\n'); + if (--numFilesToRead === 0) cb(sourceFiles); + }); + }); +} + +// This is basically just `trim_line` from https://github.com/getsentry/sentry/blob/master/src/sentry/lang/javascript/processor.py#L67 +function snipLine(line, colno) { + var ll = line.length; + if (ll <= 150) return line; + if (colno > ll) colno = ll; + + var start = Math.max(colno - 60, 0); + if (start < 5) start = 0; + + var end = Math.min(start + 140, ll); + if (end > ll - 5) end = ll; + if (end === ll) start = Math.max(end - 140, 0); + + line = line.slice(start, end); + if (start > 0) line = '{snip} ' + line; + if (end < ll) line += ' {snip}'; + + return line; +} + +function snipLine0(line) { + return snipLine(line, 0); +} + +function parseStack(err, cb) { + if (!err) return cb([]); + + var stack = stacktrace.parse(err); + if (!stack || !Array.isArray(stack) || !stack.length || !stack[0].getFileName) { + // the stack is not the useful thing we were expecting :/ + return cb([]); + } + + // Sentry expects the stack trace to be oldest -> newest, v8 provides newest -> oldest + stack.reverse(); + + var frames = []; + var filesToRead = {}; + stack.forEach(function(line) { + var frame = { + filename: line.getFileName() || '', + lineno: line.getLineNumber(), + colno: line.getColumnNumber(), + function: getFunction(line) + }; + + var isInternal = + line.isNative() || + (frame.filename[0] !== '/' && + frame.filename[0] !== '.' && + frame.filename.indexOf(':\\') !== 1); + + // in_app is all that's not an internal Node function or a module within node_modules + // note that isNative appears to return true even for node core libraries + // see https://github.com/getsentry/raven-node/issues/176 + frame.in_app = !isInternal && frame.filename.indexOf('node_modules/') === -1; + + // Extract a module name based on the filename + if (frame.filename) { + frame.module = getModule(frame.filename); + if (!isInternal) filesToRead[frame.filename] = true; + } + + frames.push(frame); + }); + + return readSourceFiles(Object.keys(filesToRead), function(sourceFiles) { + frames.forEach(function(frame) { + if (frame.filename && sourceFiles[frame.filename]) { + var lines = sourceFiles[frame.filename]; + try { + frame.pre_context = lines + .slice(Math.max(0, frame.lineno - (LINES_OF_CONTEXT + 1)), frame.lineno - 1) + .map(snipLine0); + frame.context_line = snipLine(lines[frame.lineno - 1], frame.colno); + frame.post_context = lines + .slice(frame.lineno, frame.lineno + LINES_OF_CONTEXT) + .map(snipLine0); + } catch (e) { + // anomaly, being defensive in case + // unlikely to ever happen in practice but can definitely happen in theory + } + } + }); + + cb(frames); + }); +} + +// expose basically for testing because I don't know what I'm doing +module.exports.parseStack = parseStack; +module.exports.getModule = getModule; + + +/***/ }), +/* 1 */ +/***/ (function(module, exports) { + +module.exports = require("util"); + +/***/ }), +/* 2 */ +/***/ (function(module, exports) { + +module.exports = require("path"); + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var events = __webpack_require__(9); +var util = __webpack_require__(1); +var timeoutReq = __webpack_require__(19); + +var http = __webpack_require__(10); +var https = __webpack_require__(20); + +var agentOptions = {keepAlive: true, maxSockets: 100}; +var httpAgent = new http.Agent(agentOptions); +var httpsAgent = new https.Agent(agentOptions); + +function Transport() {} +util.inherits(Transport, events.EventEmitter); + +function HTTPTransport(options) { + this.defaultPort = 80; + this.transport = http; + this.options = options || {}; + this.agent = httpAgent; +} +util.inherits(HTTPTransport, Transport); +HTTPTransport.prototype.send = function(client, message, headers, eventId, cb) { + var options = { + hostname: client.dsn.host, + path: client.dsn.path + 'api/' + client.dsn.project_id + '/store/', + headers: headers, + method: 'POST', + port: client.dsn.port || this.defaultPort, + ca: client.ca, + agent: this.agent + }; + for (var key in this.options) { + if (this.options.hasOwnProperty(key)) { + options[key] = this.options[key]; + } + } + + // prevent off heap memory explosion + var _name = this.agent.getName({host: client.dsn.host, port: client.dsn.port}); + var _requests = this.agent.requests[_name]; + if (_requests && Object.keys(_requests).length > client.maxReqQueueCount) { + // other feedback strategy + client.emit('error', new Error('client req queue is full..')); + return; + } + + var req = this.transport.request(options, function(res) { + res.setEncoding('utf8'); + if (res.statusCode >= 200 && res.statusCode < 300) { + client.emit('logged', eventId); + cb && cb(null, eventId); + } else { + var reason = res.headers['x-sentry-error']; + var e = new Error('HTTP Error (' + res.statusCode + '): ' + reason); + e.response = res; + e.statusCode = res.statusCode; + e.reason = reason; + e.sendMessage = message; + e.requestHeaders = headers; + e.eventId = eventId; + client.emit('error', e); + cb && cb(e); + } + + // force the socket to drain + var noop = function() {}; + res.on('data', noop); + res.on('end', noop); + }); + + timeoutReq(req, client.sendTimeout * 1000); + + var cbFired = false; + req.on('error', function(e) { + client.emit('error', e); + if (!cbFired) { + cb && cb(e); + cbFired = true; + } + }); + req.end(message); +}; + +function HTTPSTransport(options) { + this.defaultPort = 443; + this.transport = https; + this.options = options || {}; + this.agent = httpsAgent; +} +util.inherits(HTTPSTransport, HTTPTransport); + +module.exports.http = new HTTPTransport(); +module.exports.https = new HTTPSTransport(); +module.exports.Transport = Transport; +module.exports.HTTPTransport = HTTPTransport; +module.exports.HTTPSTransport = HTTPSTransport; + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var utils = __webpack_require__(0); + +var defaultOnConfig = { + console: true +}; + +var defaultConfig = { + console: false, + http: false, + pg: false +}; + +function instrument(Raven, config) { + if (config === false) { + return; + } else if (config === true) { + config = defaultOnConfig; + } else { + config = utils.extend({}, defaultConfig, config); + } + + Raven.instrumentedOriginals = []; + Raven.instrumentedModules = []; + + var Module = __webpack_require__(28); + utils.fill( + Module, + '_load', + function(origLoad) { + return function(moduleId, parent, isMain) { + var origModule = origLoad.apply(this, arguments); + if (config[moduleId] && Raven.instrumentedModules.indexOf(moduleId) === -1) { + Raven.instrumentedModules.push(moduleId); + return __webpack_require__(29)("./" + moduleId)(Raven, origModule, Raven.instrumentedOriginals); + } + return origModule; + }; + }, + Raven.instrumentedOriginals + ); + + // special case: since console is built-in and app-level code won't require() it, do that here + if (config.console) { + __webpack_require__(30); + } + + // observation: when the https module does its own require('http'), it *does not* hit our hooked require to instrument http on the fly + // but if we've previously instrumented http, https *does* get our already-instrumented version + // this is because raven's transports are required before this instrumentation takes place, which loads https (and http) + // so module cache will have uninstrumented http; proactively loading it here ensures instrumented version is in module cache + // alternatively we could refactor to load our transports later, but this is easier and doesn't have much drawback + if (config.http) { + __webpack_require__(10); + } +} + +function deinstrument(Raven) { + if (!Raven.instrumentedOriginals) return; + var original; + // eslint-disable-next-line no-cond-assign + while ((original = Raven.instrumentedOriginals.shift())) { + var obj = original[0]; + var name = original[1]; + var orig = original[2]; + obj[name] = orig; + } +} + +module.exports = { + instrument: instrument, + deinstrument: deinstrument +}; + + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/* + json-stringify-safe + Like JSON.stringify, but doesn't throw on circular references. + + Originally forked from https://github.com/isaacs/json-stringify-safe + version 5.0.1 on 2017-09-21 and modified to handle Errors serialization. + Tests for this are in test/vendor. + + ISC license: https://github.com/isaacs/json-stringify-safe/blob/master/LICENSE + */ + +exports = module.exports = stringify; +exports.getSerialize = serializer; + +function stringify(obj, replacer, spaces, cycleReplacer) { + return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces); +} + +// https://github.com/ftlabs/js-abbreviate/blob/fa709e5f139e7770a71827b1893f22418097fbda/index.js#L95-L106 +function stringifyError(value) { + var err = { + // These properties are implemented as magical getters and don't show up in for in + stack: value.stack, + message: value.message, + name: value.name + }; + + for (var i in value) { + if (Object.prototype.hasOwnProperty.call(value, i)) { + err[i] = value[i]; + } + } + + return err; +} + +function serializer(replacer, cycleReplacer) { + var stack = []; + var keys = []; + + if (cycleReplacer == null) { + cycleReplacer = function(key, value) { + if (stack[0] === value) { + return '[Circular ~]'; + } + return '[Circular ~.' + keys.slice(0, stack.indexOf(value)).join('.') + ']'; + }; + } + + return function(key, value) { + if (stack.length > 0) { + var thisPos = stack.indexOf(this); + ~thisPos ? stack.splice(thisPos + 1) : stack.push(this); + ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key); + + if (~stack.indexOf(value)) { + value = cycleReplacer.call(this, key, value); + } + } else { + stack.push(value); + } + + return replacer == null + ? value instanceof Error ? stringifyError(value) : value + : replacer.call(this, key, value); + }; +} + + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var cookie = __webpack_require__(18); +var urlParser = __webpack_require__(7); +var stringify = __webpack_require__(5); + +var utils = __webpack_require__(0); + +module.exports.parseText = function parseText(message, kwargs) { + kwargs = kwargs || {}; + kwargs.message = message; + + return kwargs; +}; + +module.exports.parseError = function parseError(err, kwargs, cb) { + utils.parseStack(err, function(frames) { + var name = + ({}.hasOwnProperty.call(err, 'name') ? err.name : err.constructor.name) + ''; + if (typeof kwargs.message === 'undefined') { + kwargs.message = name + ': ' + (err.message || ''); + } + kwargs.exception = [ + { + type: name, + value: err.message, + stacktrace: { + frames: frames + } + } + ]; + + // Save additional error properties to `extra` under the error type (e.g. `extra.AttributeError`) + var extraErrorProps; + for (var key in err) { + if (err.hasOwnProperty(key)) { + if (key !== 'name' && key !== 'message' && key !== 'stack' && key !== 'domain') { + extraErrorProps = extraErrorProps || {}; + extraErrorProps[key] = err[key]; + } + } + } + if (extraErrorProps) { + kwargs.extra = kwargs.extra || {}; + kwargs.extra[name] = extraErrorProps; + } + + for (var n = frames.length - 1; n >= 0; n--) { + if (frames[n].in_app) { + kwargs.culprit = kwargs.culprit || utils.getCulprit(frames[n]); + break; + } + } + + cb(kwargs); + }); +}; + +module.exports.parseRequest = function parseRequest(req, parseUser) { + var kwargs = {}; + + // headers: + // node, express: req.headers + // koa: req.header + var headers = req.headers || req.header || {}; + + // method: + // node, express, koa: req.method + var method = req.method; + + // host: + // express: req.hostname in > 4 and req.host in < 4 + // koa: req.host + // node: req.headers.host + var host = req.hostname || req.host || headers.host || ''; + + // protocol: + // node: + // express, koa: req.protocol + var protocol = + req.protocol === 'https' || req.secure || (req.socket || {}).encrypted + ? 'https' + : 'http'; + + // url (including path and query string): + // node, express: req.originalUrl + // koa: req.url + var originalUrl = req.originalUrl || req.url; + + // absolute url + var absoluteUrl = protocol + '://' + host + originalUrl; + + // query string: + // node: req.url (raw) + // express, koa: req.query + var query = req.query || urlParser.parse(originalUrl || '', true).query; + + // cookies: + // node, express, koa: req.headers.cookie + var cookies = cookie.parse(headers.cookie || ''); + + // body data: + // node, express, koa: req.body + var data = req.body; + if (['GET', 'HEAD'].indexOf(method) === -1) { + if (typeof data === 'undefined') { + data = ''; + } + } + + if (data && typeof data !== 'string' && {}.toString.call(data) !== '[object String]') { + // Make sure the request body is a string + data = stringify(data); + } + + // http interface + var http = { + method: method, + query_string: query, + headers: headers, + cookies: cookies, + data: data, + url: absoluteUrl + }; + + // expose http interface + kwargs.request = http; + + // user: typically found on req.user in express/passport patterns + // five cases for parseUser value: + // absent: grab only id, username, email from req.user + // false: capture nothing + // true: capture all keys from req.user + // array: provided whitelisted keys to grab from req.user + // function :: req -> user: custom parsing function + if (parseUser == null) parseUser = ['id', 'username', 'email']; + if (parseUser) { + var user = {}; + if (typeof parseUser === 'function') { + user = parseUser(req); + } else if (req.user) { + if (parseUser === true) { + for (var key in req.user) { + if ({}.hasOwnProperty.call(req.user, key)) { + user[key] = req.user[key]; + } + } + } else { + parseUser.forEach(function(fieldName) { + if ({}.hasOwnProperty.call(req.user, fieldName)) { + user[fieldName] = req.user[fieldName]; + } + }); + } + } + + // client ip: + // node: req.connection.remoteAddress + // express, koa: req.ip + var ip = req.ip || (req.connection && req.connection.remoteAddress); + if (ip) { + user.ip_address = ip; + } + + kwargs.user = user; + } + + return kwargs; +}; + + +/***/ }), +/* 7 */ +/***/ (function(module, exports) { + +module.exports = require("url"); + +/***/ }), +/* 8 */ +/***/ (function(module, exports) { + +module.exports = require("fs"); + +/***/ }), +/* 9 */ +/***/ (function(module, exports) { + +module.exports = require("events"); + +/***/ }), +/* 10 */ +/***/ (function(module, exports) { + +module.exports = require("http"); + +/***/ }), +/* 11 */ +/***/ (function(module, exports) { + +module.exports = {"_from":"raven@^2.2.1","_id":"raven@2.2.1","_inBundle":false,"_integrity":"sha1-V8f75oqAFH7FJ97z18AVdc+Uj+M=","_location":"/raven","_phantomChildren":{},"_requested":{"type":"range","registry":true,"raw":"raven@^2.2.1","name":"raven","escapedName":"raven","rawSpec":"^2.2.1","saveSpec":null,"fetchSpec":"^2.2.1"},"_requiredBy":["#USER","/"],"_resolved":"https://registry.npmjs.org/raven/-/raven-2.2.1.tgz","_shasum":"57c7fbe68a80147ec527def3d7c01575cf948fe3","_spec":"raven@^2.2.1","_where":"/Users/kamilogorek/Projects/sentry/repros/node-sourcemaps","author":{"name":"Matt Robenolt","email":"matt@ydekproductions.com"},"bin":{"raven":"./bin/raven"},"bugs":{"url":"https://github.com/getsentry/raven-node/issues"},"bundleDependencies":false,"dependencies":{"cookie":"0.3.1","lsmod":"1.0.0","stack-trace":"0.0.9","timed-out":"4.0.1","uuid":"3.0.0"},"deprecated":false,"description":"A standalone (Node.js) client for Sentry","devDependencies":{"coffee-script":"~1.10.0","connect":"*","eslint":"^4.5.0","eslint-config-prettier":"^2.3.0","express":"*","glob":"~3.1.13","husky":"^0.14.3","istanbul":"^0.4.3","lint-staged":"^4.0.4","mocha":"~3.1.2","nock":"~9.0.0","prettier":"^1.6.1","should":"11.2.0","sinon":"^3.3.0"},"engines":{"node":">= 4.0.0"},"homepage":"https://github.com/getsentry/raven-node","keywords":["debugging","errors","exceptions","logging","raven","sentry"],"license":"BSD-2-Clause","lint-staged":{"*.js":["prettier --write","git add"]},"main":"index.js","name":"raven","prettier":{"singleQuote":true,"bracketSpacing":false,"printWidth":90},"repository":{"type":"git","url":"git://github.com/getsentry/raven-node.git"},"scripts":{"lint":"node_modules/eslint/bin/eslint.js .","precommit":"lint-staged","pretest":"npm install && npm run lint","test":"NODE_ENV=test istanbul cover _mocha -- --reporter dot && NODE_ENV=test node_modules/coffee-script/bin/coffee ./test/run.coffee","test-full":"npm run test && cd test/instrumentation && ./run.sh","test-mocha":"NODE_ENV=test mocha"},"version":"2.2.1"} + +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var util = __webpack_require__(1); +var utils = __webpack_require__(0); + +module.exports = function(Raven, console, originals) { + var wrapConsoleMethod = function(level) { + if (!(level in console)) { + return; + } + + utils.fill( + console, + level, + function(originalConsoleLevel) { + var sentryLevel = level === 'warn' ? 'warning' : level; + + return function() { + var args = [].slice.call(arguments); + + Raven.captureBreadcrumb({ + message: util.format.apply(null, args), + level: sentryLevel, + category: 'console' + }); + + originalConsoleLevel.apply(console, args); + }; + }, + originals + ); + }; + + ['debug', 'info', 'warn', 'error', 'log'].forEach(wrapConsoleMethod); + + return console; +}; + + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var util = __webpack_require__(1); +var utils = __webpack_require__(0); + +module.exports = function(Raven, http, originals) { + var OrigClientRequest = http.ClientRequest; + var ClientRequest = function(options, cb) { + // Note: this won't capture a breadcrumb if a response never comes + // It would be useful to know if that was the case, though, so + // todo: revisit to see if we can capture sth indicating response never came + // possibility: capture one breadcrumb for "req sent" and one for "res recvd" + // seems excessive but solves the problem and *is* strictly more information + // could be useful for weird response sequencing bug scenarios + OrigClientRequest.call(this, options, cb); + + // We could just always reconstruct this from this.agent, this._headers, this.path, etc + // but certain other http-instrumenting libraries (like nock, which we use for tests) fail to + // maintain the guarantee that after calling OrigClientRequest, those fields will be populated + if (typeof options === 'string') { + this.__ravenBreadcrumbUrl = options; + } else { + this.__ravenBreadcrumbUrl = + (options.protocol || '') + + '//' + + (options.hostname || options.host || '') + + (options.path || '/'); + } + }; + util.inherits(ClientRequest, OrigClientRequest); + + utils.fill(ClientRequest.prototype, 'emit', function(origEmit) { + return function(evt, maybeResp) { + if (evt === 'response' && this.__ravenBreadcrumbUrl) { + if (!Raven.dsn || this.__ravenBreadcrumbUrl.indexOf(Raven.dsn.host) === -1) { + Raven.captureBreadcrumb({ + type: 'http', + category: 'http', + data: { + method: this.method, + url: this.__ravenBreadcrumbUrl, + status_code: maybeResp.statusCode + } + }); + } + } + return origEmit.apply(this, arguments); + }; + }); + + utils.fill( + http, + 'ClientRequest', + function() { + return ClientRequest; + }, + originals + ); + + // http.request orig refs module-internal ClientRequest, not exported one, so + // it still points at orig ClientRequest after our monkeypatch; these reimpls + // just get that reference updated to use our new ClientRequest + utils.fill( + http, + 'request', + function() { + return function(options, cb) { + return new http.ClientRequest(options, cb); + }; + }, + originals + ); + + utils.fill( + http, + 'get', + function() { + return function(options, cb) { + var req = http.request(options, cb); + req.end(); + return req; + }; + }, + originals + ); + + return http; +}; + + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +module.exports = function(Raven, pg, originals) { + // Using fill helper here is hard because of `this` binding + var origQuery = pg.Connection.prototype.query; + pg.Connection.prototype.query = function(text) { + Raven.captureBreadcrumb({ + category: 'postgres', + message: text + }); + origQuery.call(this, text); + }; + // todo thread this through + // originals.push([pg.Connection.prototype, 'query', origQuery]); +}; + + +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + +var Raven = __webpack_require__(16); +var path = __webpack_require__(2); +var foo = __webpack_require__(32); + +Raven.config( + 'http://36dfaa7c54664f429aac79ac89d7fb68:b4505a72a8ce4ecd8deb8038124b0909@localhost:8000/8', + { + release: process.env.RELEASE, + dataCallback: function(data) { + var stacktrace = data.exception && data.exception[0].stacktrace; + + if (stacktrace && stacktrace.frames) { + stacktrace.frames.forEach(function(frame) { + if (frame.filename.startsWith('/')) { + frame.filename = 'app:///' + path.basename(frame.filename); + } + }); + } + + console.log(JSON.stringify(data, null, 2)) + + return data; + }, + shouldSendCallback: function() { + return 'false'; + }, + }, +).install(); + +function App() { + foo(); +} + +App(); + +setTimeout(function() { + App(); +}, 500); + + + +/***/ }), +/* 16 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +module.exports = __webpack_require__(17); +module.exports.utils = __webpack_require__(0); + +module.exports.transports = __webpack_require__(3); +module.exports.parsers = __webpack_require__(6); + +// To infinity and beyond +Error.stackTraceLimit = Infinity; + + +/***/ }), +/* 17 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var stringify = __webpack_require__(5); +var parsers = __webpack_require__(6); +var zlib = __webpack_require__(23); +var utils = __webpack_require__(0); +var uuid = __webpack_require__(24); +var transports = __webpack_require__(3); +var nodeUtil = __webpack_require__(1); // nodeUtil to avoid confusion with "utils" +var events = __webpack_require__(9); +var domain = __webpack_require__(27); + +var instrumentor = __webpack_require__(4); + +var extend = utils.extend; + +function Raven() { + this.breadcrumbs = { + record: this.captureBreadcrumb.bind(this) + }; +} + +nodeUtil.inherits(Raven, events.EventEmitter); + +extend(Raven.prototype, { + config: function config(dsn, options) { + // We get lots of users using raven-node when they want raven-js, hence this warning if it seems like a browser + if ( + typeof window !== 'undefined' && + typeof document !== 'undefined' && + typeof navigator !== 'undefined' + ) { + utils.consoleAlertOnce( + "This looks like a browser environment; are you sure you don't want Raven.js for browser JavaScript? https://sentry.io/for/javascript" + ); + } + + if (arguments.length === 0) { + // no arguments, use default from environment + dsn = process.env.SENTRY_DSN; + options = {}; + } + if (typeof dsn === 'object') { + // They must only be passing through options + options = dsn; + dsn = process.env.SENTRY_DSN; + } + options = options || {}; + + this.raw_dsn = dsn; + this.dsn = utils.parseDSN(dsn); + this.name = options.name || process.env.SENTRY_NAME || __webpack_require__(31).hostname(); + this.root = options.root || process.cwd(); + this.transport = options.transport || transports[this.dsn.protocol]; + this.sendTimeout = options.sendTimeout || 1; + this.release = options.release || process.env.SENTRY_RELEASE || ''; + this.environment = + options.environment || process.env.SENTRY_ENVIRONMENT || process.env.NODE_ENV || ''; + + // autoBreadcrumbs: true enables all, autoBreadcrumbs: false disables all + // autoBreadcrumbs: { http: true } enables a single type + this.autoBreadcrumbs = options.autoBreadcrumbs || false; + // default to 30, don't allow higher than 100 + this.maxBreadcrumbs = Math.max(0, Math.min(options.maxBreadcrumbs || 30, 100)); + + this.captureUnhandledRejections = options.captureUnhandledRejections; + this.loggerName = options.logger || ''; + this.dataCallback = options.dataCallback; + this.shouldSendCallback = options.shouldSendCallback; + this.sampleRate = typeof options.sampleRate === 'undefined' ? 1 : options.sampleRate; + this.maxReqQueueCount = options.maxReqQueueCount || 100; + this.parseUser = options.parseUser; + + if (!this.dsn) { + utils.consoleAlert('no DSN provided, error reporting disabled'); + } + + if (this.dsn.protocol === 'https') { + // In case we want to provide our own SSL certificates / keys + this.ca = options.ca || null; + } + + // enabled if a dsn is set + this._enabled = !!this.dsn; + + var globalContext = (this._globalContext = {}); + if (options.tags) { + globalContext.tags = options.tags; + } + if (options.extra) { + globalContext.extra = options.extra; + } + + this.onFatalError = this.defaultOnFatalError = function(err, sendErr, eventId) { + console.error(err && err.stack ? err.stack : err); + process.exit(1); + }; + this.uncaughtErrorHandler = this.makeErrorHandler(); + + this.on('error', function(err) { + utils.consoleAlert('failed to send exception to sentry: ' + err.message); + }); + + return this; + }, + + install: function install(cb) { + if (this.installed) return this; + + if (typeof cb === 'function') { + this.onFatalError = cb; + } + + process.on('uncaughtException', this.uncaughtErrorHandler); + + if (this.captureUnhandledRejections) { + var self = this; + process.on('unhandledRejection', function(reason) { + self.captureException(reason, function(sendErr, eventId) { + if (!sendErr) utils.consoleAlert('unhandledRejection captured: ' + eventId); + }); + }); + } + + instrumentor.instrument(this, this.autoBreadcrumbs); + + this.installed = true; + + return this; + }, + + uninstall: function uninstall() { + if (!this.installed) return this; + + instrumentor.deinstrument(this); + + // todo: this works for tests for now, but isn't what we ultimately want to be doing + process.removeAllListeners('uncaughtException'); + process.removeAllListeners('unhandledRejection'); + + this.installed = false; + + return this; + }, + + makeErrorHandler: function() { + var self = this; + var caughtFirstError = false; + var caughtSecondError = false; + var calledFatalError = false; + var firstError; + return function(err) { + if (!caughtFirstError) { + // this is the first uncaught error and the ultimate reason for shutting down + // we want to do absolutely everything possible to ensure it gets captured + // also we want to make sure we don't go recursion crazy if more errors happen after this one + firstError = err; + caughtFirstError = true; + self.captureException(err, function(sendErr, eventId) { + if (!calledFatalError) { + calledFatalError = true; + self.onFatalError(err, sendErr, eventId); + } + }); + } else if (calledFatalError) { + // we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down + utils.consoleAlert( + 'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown' + ); + self.defaultOnFatalError(err); + } else if (!caughtSecondError) { + // two cases for how we can hit this branch: + // - capturing of first error blew up and we just caught the exception from that + // - quit trying to capture, proceed with shutdown + // - a second independent error happened while waiting for first error to capture + // - want to avoid causing premature shutdown before first error capture finishes + // it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff + // so let's instead just delay a bit before we proceed with our action here + // in case 1, we just wait a bit unnecessarily but ultimately do the same thing + // in case 2, the delay hopefully made us wait long enough for the capture to finish + // two potential nonideal outcomes: + // nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError + // nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error + // note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError) + // we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish + caughtSecondError = true; + setTimeout(function() { + if (!calledFatalError) { + // it was probably case 1, let's treat err as the sendErr and call onFatalError + calledFatalError = true; + self.onFatalError(firstError, err); + } else { + // it was probably case 2, our first error finished capturing while we waited, cool, do nothing + } + }, (self.sendTimeout + 1) * 1000); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc + } + }; + }, + + generateEventId: function generateEventId() { + return uuid().replace(/-/g, ''); + }, + + process: function process(eventId, kwargs, cb) { + // prod codepaths shouldn't hit this branch, for testing + if (typeof eventId === 'object') { + cb = kwargs; + kwargs = eventId; + eventId = this.generateEventId(); + } + + var domainContext = (domain.active && domain.active.sentryContext) || {}; + kwargs.user = extend({}, this._globalContext.user, domainContext.user, kwargs.user); + kwargs.tags = extend({}, this._globalContext.tags, domainContext.tags, kwargs.tags); + kwargs.extra = extend( + {}, + this._globalContext.extra, + domainContext.extra, + kwargs.extra + ); + kwargs.breadcrumbs = { + values: domainContext.breadcrumbs || this._globalContext.breadcrumbs || [] + }; + + /* + `request` is our specified property name for the http interface: https://docs.sentry.io/clientdev/interfaces/http/ + `req` is the conventional name for a request object in node/express/etc + we want to enable someone to pass a `request` property to kwargs according to http interface + but also want to provide convenience for passing a req object and having us parse it out + so we only parse a `req` property if the `request` property is absent/empty (and hence we won't clobber) + parseUser returns a partial kwargs object with a `request` property and possibly a `user` property + */ + kwargs.request = this._createRequestObject( + this._globalContext.request, + domainContext.request, + kwargs.request + ); + if (Object.keys(kwargs.request).length === 0) { + var req = this._createRequestObject( + this._globalContext.req, + domainContext.req, + kwargs.req + ); + if (Object.keys(req).length > 0) { + var parseUser = Object.keys(kwargs.user).length === 0 ? this.parseUser : false; + extend(kwargs, parsers.parseRequest(req, parseUser)); + delete kwargs.req; + } + } + + kwargs.modules = utils.getModules(); + kwargs.server_name = kwargs.server_name || this.name; + + if (typeof process.version !== 'undefined') { + kwargs.extra.node = process.version; + } + + kwargs.environment = kwargs.environment || this.environment; + kwargs.logger = kwargs.logger || this.loggerName; + kwargs.event_id = eventId; + kwargs.timestamp = new Date().toISOString().split('.')[0]; + kwargs.project = this.dsn.project_id; + kwargs.platform = 'node'; + + // Only include release information if it is set + if (this.release) { + kwargs.release = this.release; + } + + if (this.dataCallback) { + kwargs = this.dataCallback(kwargs); + } + + var shouldSend = true; + if (!this._enabled) shouldSend = false; + if (this.shouldSendCallback && !this.shouldSendCallback(kwargs)) shouldSend = false; + if (Math.random() >= this.sampleRate) shouldSend = false; + + if (shouldSend) { + this.send(kwargs, cb); + } else { + // wish there was a good way to communicate to cb why we didn't send; worth considering cb api change? + // could be shouldSendCallback, could be disabled, could be sample rate + // avoiding setImmediate here because node 0.8 + cb && + setTimeout(function() { + cb(null, eventId); + }, 0); + } + }, + + send: function send(kwargs, cb) { + var self = this; + var skwargs = stringify(kwargs); + var eventId = kwargs.event_id; + + zlib.deflate(skwargs, function(err, buff) { + var message = buff.toString('base64'), + timestamp = new Date().getTime(), + headers = { + 'X-Sentry-Auth': utils.getAuthHeader( + timestamp, + self.dsn.public_key, + self.dsn.private_key + ), + 'Content-Type': 'application/octet-stream', + 'Content-Length': message.length + }; + + self.transport.send(self, message, headers, eventId, cb); + }); + }, + + captureMessage: function captureMessage(message, kwargs, cb) { + if (!cb && typeof kwargs === 'function') { + cb = kwargs; + kwargs = {}; + } else { + kwargs = kwargs || {}; + } + var eventId = this.generateEventId(); + this.process(eventId, parsers.parseText(message, kwargs), cb); + + return eventId; + }, + + captureException: function captureException(err, kwargs, cb) { + if (!(err instanceof Error)) { + // This handles when someone does: + // throw "something awesome"; + // We synthesize an Error here so we can extract a (rough) stack trace. + err = new Error(err); + } + + if (!cb && typeof kwargs === 'function') { + cb = kwargs; + kwargs = {}; + } else { + kwargs = kwargs || {}; + } + + var self = this; + var eventId = this.generateEventId(); + parsers.parseError(err, kwargs, function(kw) { + self.process(eventId, kw, cb); + }); + + return eventId; + }, + + context: function(ctx, func) { + if (!func && typeof ctx === 'function') { + func = ctx; + ctx = {}; + } + + // todo/note: raven-js takes an args param to do apply(this, args) + // i don't think it's correct/necessary to bind this to the wrap call + // and i don't know if we need to support the args param; it's undocumented + return this.wrap(ctx, func).apply(null); + }, + + wrap: function(options, func) { + if (!func && typeof options === 'function') { + func = options; + options = {}; + } + + var wrapDomain = domain.create(); + // todo: better property name than sentryContext, maybe __raven__ or sth? + wrapDomain.sentryContext = options; + + wrapDomain.on('error', this.uncaughtErrorHandler); + var wrapped = wrapDomain.bind(func); + + for (var property in func) { + if ({}.hasOwnProperty.call(func, property)) { + wrapped[property] = func[property]; + } + } + wrapped.prototype = func.prototype; + wrapped.__raven__ = true; + wrapped.__inner__ = func; + // note: domain.bind sets wrapped.domain, but it's not documented, unsure if we should rely on that + wrapped.__domain__ = wrapDomain; + + return wrapped; + }, + + interceptErr: function(options, func) { + if (!func && typeof options === 'function') { + func = options; + options = {}; + } + var self = this; + var wrapped = function() { + var err = arguments[0]; + if (err instanceof Error) { + self.captureException(err, options); + } else { + func.apply(null, arguments); + } + }; + + // repetitive with wrap + for (var property in func) { + if ({}.hasOwnProperty.call(func, property)) { + wrapped[property] = func[property]; + } + } + wrapped.prototype = func.prototype; + wrapped.__raven__ = true; + wrapped.__inner__ = func; + + return wrapped; + }, + + setContext: function setContext(ctx) { + if (domain.active) { + domain.active.sentryContext = ctx; + } else { + this._globalContext = ctx; + } + return this; + }, + + mergeContext: function mergeContext(ctx) { + extend(this.getContext(), ctx); + return this; + }, + + getContext: function getContext() { + if (domain.active) { + if (!domain.active.sentryContext) { + domain.active.sentryContext = {}; + utils.consoleAlert('sentry context not found on active domain'); + } + return domain.active.sentryContext; + } + return this._globalContext; + }, + + setCallbackHelper: function(propertyName, callback) { + var original = this[propertyName]; + if (typeof callback === 'function') { + this[propertyName] = function(data) { + return callback(data, original); + }; + } else { + this[propertyName] = callback; + } + + return this; + }, + + /* + * Set the dataCallback option + * + * @param {function} callback The callback to run which allows the + * data blob to be mutated before sending + * @return {Raven} + */ + setDataCallback: function(callback) { + return this.setCallbackHelper('dataCallback', callback); + }, + + /* + * Set the shouldSendCallback option + * + * @param {function} callback The callback to run which allows + * introspecting the blob before sending + * @return {Raven} + */ + setShouldSendCallback: function(callback) { + return this.setCallbackHelper('shouldSendCallback', callback); + }, + + requestHandler: function() { + var self = this; + return function(req, res, next) { + self.context({req: req}, function() { + domain.active.add(req); + domain.active.add(res); + next(); + }); + }; + }, + + errorHandler: function() { + var self = this; + return function(err, req, res, next) { + var status = err.status || err.statusCode || err.status_code || 500; + + // skip anything not marked as an internal server error + if (status < 500) return next(err); + + var eventId = self.captureException(err, {req: req}); + res.sentry = eventId; + return next(err); + }; + }, + + captureBreadcrumb: function(breadcrumb) { + // Avoid capturing global-scoped breadcrumbs before instrumentation finishes + if (!this.installed) return; + + breadcrumb = extend( + { + timestamp: +new Date() / 1000 + }, + breadcrumb + ); + var currCtx = this.getContext(); + if (!currCtx.breadcrumbs) currCtx.breadcrumbs = []; + currCtx.breadcrumbs.push(breadcrumb); + if (currCtx.breadcrumbs.length > this.maxBreadcrumbs) { + currCtx.breadcrumbs.shift(); + } + this.setContext(currCtx); + }, + + _createRequestObject: function() { + /** + * When using proxy, some of the attributes of req/request objects are non-enumerable. + * To make sure, that they are still available to us after we consolidate our sources + * (eg. globalContext.request + domainContext.request + kwargs.request), + * we manually pull them out from original objects. + * + * We don't use Object.assign/extend as it's only merging over objects own properties, + * and we don't want to go through all of the properties as well, as we simply don't + * need all of them. + * + * So far the only missing piece is `ip`, but we can specify what properties should + * be pulled by extending `nonEnumerables` array. + **/ + var sources = Array.from(arguments).filter(function(source) { + return Object.prototype.toString.call(source) === '[object Object]'; + }); + sources = [{}].concat(sources); + var request = extend.apply(null, sources); + var nonEnumberables = ['ip']; + + nonEnumberables.forEach(function(key) { + sources.forEach(function(source) { + if (source[key]) request[key] = source[key]; + }); + }); + + return request; + } +}); + +// Maintain old API compat, need to make sure arguments length is preserved +function Client(dsn, options) { + if (dsn instanceof Client) return dsn; + var ravenInstance = new Raven(); + return ravenInstance.config.apply(ravenInstance, arguments); +} +nodeUtil.inherits(Client, Raven); + +// Singleton-by-default but not strictly enforced +// todo these extra export props are sort of an adhoc mess, better way to manage? +var defaultInstance = new Raven(); +defaultInstance.Client = Client; +defaultInstance.version = __webpack_require__(11).version; +defaultInstance.disableConsoleAlerts = utils.disableConsoleAlerts; + +module.exports = defaultInstance; + + +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * cookie + * Copyright(c) 2012-2014 Roman Shtylman + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ + + + +/** + * Module exports. + * @public + */ + +exports.parse = parse; +exports.serialize = serialize; + +/** + * Module variables. + * @private + */ + +var decode = decodeURIComponent; +var encode = encodeURIComponent; +var pairSplitRegExp = /; */; + +/** + * RegExp to match field-content in RFC 7230 sec 3.2 + * + * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] + * field-vchar = VCHAR / obs-text + * obs-text = %x80-FF + */ + +var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/; + +/** + * Parse a cookie header. + * + * Parse the given cookie header string into an object + * The object has the various cookies as keys(names) => values + * + * @param {string} str + * @param {object} [options] + * @return {object} + * @public + */ + +function parse(str, options) { + if (typeof str !== 'string') { + throw new TypeError('argument str must be a string'); + } + + var obj = {} + var opt = options || {}; + var pairs = str.split(pairSplitRegExp); + var dec = opt.decode || decode; + + for (var i = 0; i < pairs.length; i++) { + var pair = pairs[i]; + var eq_idx = pair.indexOf('='); + + // skip things that don't look like key=value + if (eq_idx < 0) { + continue; + } + + var key = pair.substr(0, eq_idx).trim() + var val = pair.substr(++eq_idx, pair.length).trim(); + + // quoted values + if ('"' == val[0]) { + val = val.slice(1, -1); + } + + // only assign once + if (undefined == obj[key]) { + obj[key] = tryDecode(val, dec); + } + } + + return obj; +} + +/** + * Serialize data into a cookie header. + * + * Serialize the a name value pair into a cookie string suitable for + * http headers. An optional options object specified cookie parameters. + * + * serialize('foo', 'bar', { httpOnly: true }) + * => "foo=bar; httpOnly" + * + * @param {string} name + * @param {string} val + * @param {object} [options] + * @return {string} + * @public + */ + +function serialize(name, val, options) { + var opt = options || {}; + var enc = opt.encode || encode; + + if (typeof enc !== 'function') { + throw new TypeError('option encode is invalid'); + } + + if (!fieldContentRegExp.test(name)) { + throw new TypeError('argument name is invalid'); + } + + var value = enc(val); + + if (value && !fieldContentRegExp.test(value)) { + throw new TypeError('argument val is invalid'); + } + + var str = name + '=' + value; + + if (null != opt.maxAge) { + var maxAge = opt.maxAge - 0; + if (isNaN(maxAge)) throw new Error('maxAge should be a Number'); + str += '; Max-Age=' + Math.floor(maxAge); + } + + if (opt.domain) { + if (!fieldContentRegExp.test(opt.domain)) { + throw new TypeError('option domain is invalid'); + } + + str += '; Domain=' + opt.domain; + } + + if (opt.path) { + if (!fieldContentRegExp.test(opt.path)) { + throw new TypeError('option path is invalid'); + } + + str += '; Path=' + opt.path; + } + + if (opt.expires) { + if (typeof opt.expires.toUTCString !== 'function') { + throw new TypeError('option expires is invalid'); + } + + str += '; Expires=' + opt.expires.toUTCString(); + } + + if (opt.httpOnly) { + str += '; HttpOnly'; + } + + if (opt.secure) { + str += '; Secure'; + } + + if (opt.sameSite) { + var sameSite = typeof opt.sameSite === 'string' + ? opt.sameSite.toLowerCase() : opt.sameSite; + + switch (sameSite) { + case true: + str += '; SameSite=Strict'; + break; + case 'lax': + str += '; SameSite=Lax'; + break; + case 'strict': + str += '; SameSite=Strict'; + break; + default: + throw new TypeError('option sameSite is invalid'); + } + } + + return str; +} + +/** + * Try decoding a string using a decoding function. + * + * @param {string} str + * @param {function} decode + * @private + */ + +function tryDecode(str, decode) { + try { + return decode(str); + } catch (e) { + return str; + } +} + + +/***/ }), +/* 19 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +module.exports = function (req, time) { + if (req.timeoutTimer) { + return req; + } + + var delays = isNaN(time) ? time : {socket: time, connect: time}; + var host = req._headers ? (' to ' + req._headers.host) : ''; + + if (delays.connect !== undefined) { + req.timeoutTimer = setTimeout(function timeoutHandler() { + req.abort(); + var e = new Error('Connection timed out on request' + host); + e.code = 'ETIMEDOUT'; + req.emit('error', e); + }, delays.connect); + } + + // Clear the connection timeout timer once a socket is assigned to the + // request and is connected. + req.on('socket', function assign(socket) { + // Socket may come from Agent pool and may be already connected. + if (!(socket.connecting || socket._connecting)) { + connect(); + return; + } + + socket.once('connect', connect); + }); + + function clear() { + if (req.timeoutTimer) { + clearTimeout(req.timeoutTimer); + req.timeoutTimer = null; + } + } + + function connect() { + clear(); + + if (delays.socket !== undefined) { + // Abort the request if there is no activity on the socket for more + // than `delays.socket` milliseconds. + req.setTimeout(delays.socket, function socketTimeoutHandler() { + req.abort(); + var e = new Error('Socket timed out on request' + host); + e.code = 'ESOCKETTIMEDOUT'; + req.emit('error', e); + }); + } + } + + return req.on('error', clear); +}; + + +/***/ }), +/* 20 */ +/***/ (function(module, exports) { + +module.exports = require("https"); + +/***/ }), +/* 21 */ +/***/ (function(module, exports, __webpack_require__) { + +// builtin +var fs = __webpack_require__(8); +var path = __webpack_require__(2); + +// node 0.6 support +fs.existsSync = fs.existsSync || path.existsSync; + +// main_paths are the paths where our mainprog will be able to load from +// we store these to avoid grabbing the modules that were loaded as a result +// of a dependency module loading its dependencies, we only care about deps our +// mainprog loads +var main_paths = __webpack_require__.c[__webpack_require__.s] && __webpack_require__.c[__webpack_require__.s].paths || []; + +module.exports = function() { + var paths = Object.keys(__webpack_require__.c || []); + + // module information + var infos = {}; + + // paths we have already inspected to avoid traversing again + var seen = {}; + + paths.forEach(function(p) { + var dir = p; + + (function updir() { + var orig = dir; + dir = path.dirname(orig); + + if (!dir || orig === dir || seen[orig]) { + return; + } + else if (main_paths.indexOf(dir) < 0) { + return updir(); + } + + var pkgfile = path.join(orig, 'package.json'); + var exists = fs.existsSync(pkgfile); + + seen[orig] = true; + + // travel up the tree if no package.json here + if (!exists) { + return updir(); + } + + try { + var info = JSON.parse(fs.readFileSync(pkgfile, 'utf8')); + infos[info.name] = info.version; + } catch (e) {}; + })(); + }); + + return infos; +}; + + +/***/ }), +/* 22 */ +/***/ (function(module, exports) { + +exports.get = function(belowFn) { + var oldLimit = Error.stackTraceLimit; + Error.stackTraceLimit = Infinity; + + var dummyObject = {}; + + var v8Handler = Error.prepareStackTrace; + Error.prepareStackTrace = function(dummyObject, v8StackTrace) { + return v8StackTrace; + }; + Error.captureStackTrace(dummyObject, belowFn || exports.get); + + var v8StackTrace = dummyObject.stack; + Error.prepareStackTrace = v8Handler; + Error.stackTraceLimit = oldLimit; + + return v8StackTrace; +}; + +exports.parse = function(err) { + if (!err.stack) { + return []; + } + + var self = this; + var lines = err.stack.split('\n').slice(1); + + return lines + .map(function(line) { + if (line.match(/^\s*[-]{4,}$/)) { + return self._createParsedCallSite({ + fileName: line, + lineNumber: null, + functionName: null, + typeName: null, + methodName: null, + columnNumber: null, + 'native': null, + }); + } + + var lineMatch = line.match(/at (?:(.+)\s+)?\(?(?:(.+?):(\d+):(\d+)|([^)]+))\)?/); + if (!lineMatch) { + return; + } + + var object = null; + var method = null; + var functionName = null; + var typeName = null; + var methodName = null; + var isNative = (lineMatch[5] === 'native'); + + if (lineMatch[1]) { + var methodMatch = lineMatch[1].match(/([^\.]+)(?:\.(.+))?/); + object = methodMatch[1]; + method = methodMatch[2]; + functionName = lineMatch[1]; + typeName = 'Object'; + } + + if (method) { + typeName = object; + methodName = method; + } + + if (method === '') { + methodName = null; + functionName = ''; + } + + var properties = { + fileName: lineMatch[2] || null, + lineNumber: parseInt(lineMatch[3], 10) || null, + functionName: functionName, + typeName: typeName, + methodName: methodName, + columnNumber: parseInt(lineMatch[4], 10) || null, + 'native': isNative, + }; + + return self._createParsedCallSite(properties); + }) + .filter(function(callSite) { + return !!callSite; + }); +}; + +exports._createParsedCallSite = function(properties) { + var methods = {}; + for (var property in properties) { + var prefix = 'get'; + if (property === 'native') { + prefix = 'is'; + } + var method = prefix + property.substr(0, 1).toUpperCase() + property.substr(1); + + (function(property) { + methods[method] = function() { + return properties[property]; + } + })(property); + } + + var callSite = Object.create(methods); + for (var property in properties) { + callSite[property] = properties[property]; + } + + return callSite; +}; + + +/***/ }), +/* 23 */ +/***/ (function(module, exports) { + +module.exports = require("zlib"); + +/***/ }), +/* 24 */ +/***/ (function(module, exports, __webpack_require__) { + +// Unique ID creation requires a high quality random # generator. We feature +// detect to determine the best RNG source, normalizing to a function that +// returns 128-bits of randomness, since that's what's usually required +var _rng = __webpack_require__(25); + +// Maps for number <-> hex string conversion +var _byteToHex = []; +var _hexToByte = {}; +for (var i = 0; i < 256; ++i) { + _byteToHex[i] = (i + 0x100).toString(16).substr(1); + _hexToByte[_byteToHex[i]] = i; +} + +function buff_to_string(buf, offset) { + var i = offset || 0; + var bth = _byteToHex; + return bth[buf[i++]] + bth[buf[i++]] + + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + + bth[buf[i++]] + bth[buf[i++]] + + bth[buf[i++]] + bth[buf[i++]]; +} + +// **`v1()` - Generate time-based UUID** +// +// Inspired by https://github.com/LiosK/UUID.js +// and http://docs.python.org/library/uuid.html + +// random #'s we need to init node and clockseq +var _seedBytes = _rng(); + +// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) +var _nodeId = [ + _seedBytes[0] | 0x01, + _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5] +]; + +// Per 4.2.2, randomize (14 bit) clockseq +var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff; + +// Previous uuid creation time +var _lastMSecs = 0, _lastNSecs = 0; + +// See https://github.com/broofa/node-uuid for API details +function v1(options, buf, offset) { + var i = buf && offset || 0; + var b = buf || []; + + options = options || {}; + + var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; + + // UUID timestamps are 100 nano-second units since the Gregorian epoch, + // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so + // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' + // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. + var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime(); + + // Per 4.2.1.2, use count of uuid's generated during the current clock + // cycle to simulate higher resolution clock + var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; + + // Time since last uuid creation (in msecs) + var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000; + + // Per 4.2.1.2, Bump clockseq on clock regression + if (dt < 0 && options.clockseq === undefined) { + clockseq = clockseq + 1 & 0x3fff; + } + + // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new + // time interval + if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { + nsecs = 0; + } + + // Per 4.2.1.2 Throw error if too many uuids are requested + if (nsecs >= 10000) { + throw new Error('uuid.v1(): Can\'t create more than 10M uuids/sec'); + } + + _lastMSecs = msecs; + _lastNSecs = nsecs; + _clockseq = clockseq; + + // Per 4.1.4 - Convert from unix epoch to Gregorian epoch + msecs += 12219292800000; + + // `time_low` + var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; + b[i++] = tl >>> 24 & 0xff; + b[i++] = tl >>> 16 & 0xff; + b[i++] = tl >>> 8 & 0xff; + b[i++] = tl & 0xff; + + // `time_mid` + var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff; + b[i++] = tmh >>> 8 & 0xff; + b[i++] = tmh & 0xff; + + // `time_high_and_version` + b[i++] = tmh >>> 24 & 0xf | 0x10; // include version + b[i++] = tmh >>> 16 & 0xff; + + // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) + b[i++] = clockseq >>> 8 | 0x80; + + // `clock_seq_low` + b[i++] = clockseq & 0xff; + + // `node` + var node = options.node || _nodeId; + for (var n = 0; n < 6; ++n) { + b[i + n] = node[n]; + } + + return buf ? buf : buff_to_string(b); +} + +// **`v4()` - Generate random UUID** + +// See https://github.com/broofa/node-uuid for API details +function v4(options, buf, offset) { + // Deprecated - 'format' argument, as supported in v1.2 + var i = buf && offset || 0; + + if (typeof(options) == 'string') { + buf = options == 'binary' ? new Array(16) : null; + options = null; + } + options = options || {}; + + var rnds = options.random || (options.rng || _rng)(); + + // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` + rnds[6] = (rnds[6] & 0x0f) | 0x40; + rnds[8] = (rnds[8] & 0x3f) | 0x80; + + // Copy bytes to buffer, if provided + if (buf) { + for (var ii = 0; ii < 16; ++ii) { + buf[i + ii] = rnds[ii]; + } + } + + return buf || buff_to_string(rnds); +} + +// Export public API +var uuid = v4; +uuid.v1 = v1; +uuid.v4 = v4; + +module.exports = uuid; + + +/***/ }), +/* 25 */ +/***/ (function(module, exports, __webpack_require__) { + +var rb = __webpack_require__(26).randomBytes; +module.exports = function() { + return rb(16); +}; + + +/***/ }), +/* 26 */ +/***/ (function(module, exports) { + +module.exports = require("crypto"); + +/***/ }), +/* 27 */ +/***/ (function(module, exports) { + +module.exports = require("domain"); + +/***/ }), +/* 28 */ +/***/ (function(module, exports) { + +module.exports = require("module"); + +/***/ }), +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { + +var map = { + "./console": 12, + "./console.js": 12, + "./http": 13, + "./http.js": 13, + "./instrumentor": 4, + "./instrumentor.js": 4, + "./pg": 14, + "./pg.js": 14 +}; +function webpackContext(req) { + return __webpack_require__(webpackContextResolve(req)); +}; +function webpackContextResolve(req) { + var id = map[req]; + if(!(id + 1)) // check for number or string + throw new Error("Cannot find module '" + req + "'."); + return id; +}; +webpackContext.keys = function webpackContextKeys() { + return Object.keys(map); +}; +webpackContext.resolve = webpackContextResolve; +module.exports = webpackContext; +webpackContext.id = 29; + +/***/ }), +/* 30 */ +/***/ (function(module, exports) { + +module.exports = require("console"); + +/***/ }), +/* 31 */ +/***/ (function(module, exports) { + +module.exports = require("os"); + +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { + +var bar = __webpack_require__(33); + +function foo() { + bar(); +} + +module.exports = foo; + + +/***/ }), +/* 33 */ +/***/ (function(module, exports, __webpack_require__) { + +var path = __webpack_require__(2); + +module.exports = function bar() { + throw new Error(path.join('foo', 'bar')); +} + + +/***/ }) +/******/ ]); +//# sourceMappingURL=dist.bundle.js.map \ No newline at end of file diff --git a/tests/sentry/lang/javascript/fixtures/dist.bundle.js.map b/tests/sentry/lang/javascript/fixtures/dist.bundle.js.map new file mode 100644 index 00000000000000..d1634aa5f7240f --- /dev/null +++ b/tests/sentry/lang/javascript/fixtures/dist.bundle.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap d9a5a31d9276b73873d3","webpack:///./node_modules/raven/lib/utils.js","webpack:///external \"util\"","webpack:///external \"path\"","webpack:///./node_modules/raven/lib/transports.js","webpack:///./node_modules/raven/lib/instrumentation/instrumentor.js","webpack:///./node_modules/raven/vendor/json-stringify-safe.js","webpack:///./node_modules/raven/lib/parsers.js","webpack:///external \"url\"","webpack:///external \"fs\"","webpack:///external \"events\"","webpack:///external \"http\"","webpack:///./node_modules/raven/package.json","webpack:///./node_modules/raven/lib/instrumentation/console.js","webpack:///./node_modules/raven/lib/instrumentation/http.js","webpack:///./node_modules/raven/lib/instrumentation/pg.js","webpack:///./index.js","webpack:///./node_modules/raven/index.js","webpack:///./node_modules/raven/lib/client.js","webpack:///./node_modules/cookie/index.js","webpack:///./node_modules/timed-out/index.js","webpack:///external \"https\"","webpack:///./node_modules/lsmod/index.js","webpack:///./node_modules/stack-trace/lib/stack-trace.js","webpack:///external \"zlib\"","webpack:///./node_modules/uuid/uuid.js","webpack:///./node_modules/uuid/lib/rng.js","webpack:///external \"crypto\"","webpack:///external \"domain\"","webpack:///external \"module\"","webpack:///./node_modules/raven/lib/instrumentation ^\\.\\/.*$","webpack:///external \"console\"","webpack:///external \"os\"","webpack:///./src/foo.js","webpack:///./src/bar.js"],"names":[],"mappings":";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;AAEA;AACA;;;;;;;;AC7DA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,mBAAmB,sBAAsB;AACzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,yDAAyD;;AAEzD;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL,GAAG;AACH;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA,0BAA0B,KAAK;AAC/B,2BAA2B,KAAK;;AAEhC;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA,KAAK;;AAEL;AACA,GAAG;AACH;;AAEA;AACA;AACA;;;;;;;ACxQA,iC;;;;;;ACAA,iC;;;;;;;ACAA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA,oBAAoB;AACpB;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,kCAAkC,6CAA6C;AAC/E;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,GAAG;;AAEH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;;ACjGA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,GAAG;AACH;AACA,GAAG;AACH,4BAA4B;AAC5B;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,mDAAmD;AACnD;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;;;;;;;ACzEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;;ACpEA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,SAAS;AACT;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,mCAAmC,QAAQ;AAC3C;AACA;AACA;AACA;AACA;;AAEA;AACA,GAAG;AACH;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,+DAA+D;AAC/D;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,4CAA4C;AAC5C;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA,gBAAgB;AAChB;AACA;AACA;AACA,OAAO;AACP;AACA,gBAAgB;AAChB;AACA;AACA,SAAS;AACT;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;ACxKA,gC;;;;;;ACAA,+B;;;;;;ACAA,mC;;;;;;ACAA,iC;;;;;;ACAA,kBAAkB,wJAAwJ,eAAe,iJAAiJ,yPAAyP,0DAA0D,QAAQ,sBAAsB,SAAS,uDAAuD,4CAA4C,0FAA0F,gGAAgG,gRAAgR,YAAY,kBAAkB,wKAAwK,sCAAsC,8CAA8C,0DAA0D,eAAe,+DAA+D,YAAY,6VAA6V,mB;;;;;;;ACA59D;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA,WAAW;;AAEX;AACA;AACA,OAAO;AACP;AACA;AACA;;AAEA;;AAEA;AACA;;;;;;;;ACpCA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW;AACX;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA,iEAAiE;AACjE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA;;;;;;;;ACtFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;;;;;;;ACbA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;;AAEA;;AAEA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL,GAAG;AACH;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA,CAAC;;;;;;;;;ACrCD;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;;ACTA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,sCAA+B;AAC/B;AACA;;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,+CAA+C;AAC/C;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,yBAAyB,aAAa;AACtC;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA,iDAAiD;AACjD;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,KAAK;;AAEL;AACA,GAAG;;AAEH;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT,OAAO;AACP;;AAEA;;AAEA;;AAEA;AACA,GAAG;;AAEH;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT,OAAO;AACP;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW;AACX;AACA;AACA,SAAS,iCAAiC;AAC1C;AACA;AACA,GAAG;;AAEH;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,2BAA2B;AAC3B,2BAA2B;AAC3B;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA,KAAK;AACL,2EAA2E;AAC3E;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;AACA,GAAG;;AAEH;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,KAAK;AACL,GAAG;;AAEH;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;;AAEA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA;AACA;AACA;AACA,KAAK;;AAEL;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA,6DAA6D;AAC7D;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA,YAAY;AACZ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;;AAEA;AACA;AACA,YAAY;AACZ;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,GAAG;;AAEH;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;;AAEA;AACA,GAAG;;AAEH;AACA;AACA;AACA,aAAa,SAAS;AACtB;AACA,cAAc;AACd;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA,aAAa,SAAS;AACtB;AACA,cAAc;AACd;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA,oBAAoB,SAAS;AAC7B;AACA;AACA;AACA,OAAO;AACP;AACA,GAAG;;AAEH;AACA;AACA;AACA;;AAEA;AACA;;AAEA,gDAAgD,SAAS;AACzD;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;;AAEA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL,iBAAiB;AACjB;AACA;;AAEA;AACA;AACA;AACA,OAAO;AACP,KAAK;;AAEL;AACA;AACA,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;;;;;;;ACvjBA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA,wBAAwB;;AAExB;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,YAAY;AACZ;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA,iBAAiB,kBAAkB;AACnC;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,4BAA4B,iBAAiB;AAC7C,iBAAiB;AACjB;AACA,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,WAAW,OAAO;AAClB,YAAY;AACZ;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA,aAAa;AACb;;AAEA;AACA;AACA;AACA;;AAEA,aAAa;AACb;;AAEA;AACA;AACA;AACA;;AAEA,aAAa;AACb;;AAEA;AACA;AACA;AACA;;AAEA,aAAa;AACb;;AAEA;AACA,aAAa;AACb;;AAEA;AACA,aAAa;AACb;;AAEA;AACA;AACA;;AAEA;AACA;AACA,iBAAiB;AACjB;AACA;AACA,iBAAiB;AACjB;AACA;AACA,iBAAiB;AACjB;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,WAAW,OAAO;AAClB,WAAW,SAAS;AACpB;AACA;;AAEA;AACA;AACA;AACA,GAAG;AACH;AACA;AACA;;;;;;;;AClMA;;AAEA;AACA;AACA;AACA;;AAEA,oCAAoC;AACpC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,EAAE;;AAEF;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;;AAEA;AACA;;;;;;;ACtDA,kC;;;;;;ACAA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,aAAa;AACb,SAAS;AACT,KAAK;;AAEL;AACA;;;;;;;ACtDA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA,8BAA8B,GAAG;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS;AACT;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,KAAK;AACL;AACA;AACA,KAAK;AACL;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,KAAK;AACL;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;;;;;;AC9GA,iC;;;;;;ACAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,eAAe,SAAS;AACxB;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA,mCAAmC;AACnC;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA,iBAAiB,OAAO;AACxB;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,oBAAoB,SAAS;AAC7B;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;;;;;;AC5JA;AACA;AACA;AACA;;;;;;;ACHA,mC;;;;;;ACAA,mC;;;;;;ACAA,mC;;;;;;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uB;;;;;;ACxBA,oC;;;;;;ACAA,+B;;;;;;ACAA;;AAEA;AACA;AACA;;AAEA;;;;;;;ACNA;;AAEA;AACA;AACA","file":"dist.bundle.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 15);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap d9a5a31d9276b73873d3","'use strict';\n\nvar fs = require('fs');\nvar url = require('url');\nvar transports = require('./transports');\nvar path = require('path');\nvar lsmod = require('lsmod');\nvar stacktrace = require('stack-trace');\n\nvar ravenVersion = require('../package.json').version;\n\nvar protocolMap = {\n http: 80,\n https: 443\n};\n\nvar consoleAlerts = {};\n\nmodule.exports.disableConsoleAlerts = function disableConsoleAlerts() {\n consoleAlerts = false;\n};\n\nmodule.exports.consoleAlert = function consoleAlert(msg) {\n if (consoleAlerts) {\n console.log('raven@' + ravenVersion + ' alert: ' + msg);\n }\n};\n\nmodule.exports.consoleAlertOnce = function consoleAlertOnce(msg) {\n if (consoleAlerts && !(msg in consoleAlerts)) {\n consoleAlerts[msg] = true;\n console.log('raven@' + ravenVersion + ' alert: ' + msg);\n }\n};\n\nmodule.exports.extend =\n Object.assign ||\n function(target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n return target;\n };\n\nmodule.exports.getAuthHeader = function getAuthHeader(timestamp, apiKey, apiSecret) {\n var header = ['Sentry sentry_version=5'];\n header.push('sentry_timestamp=' + timestamp);\n header.push('sentry_client=raven-node/' + ravenVersion);\n header.push('sentry_key=' + apiKey);\n if (apiSecret) header.push('sentry_secret=' + apiSecret);\n return header.join(', ');\n};\n\nmodule.exports.parseDSN = function parseDSN(dsn) {\n if (!dsn) {\n // Let a falsey value return false explicitly\n return false;\n }\n try {\n var parsed = url.parse(dsn),\n response = {\n protocol: parsed.protocol.slice(0, -1),\n public_key: parsed.auth.split(':')[0],\n host: parsed.host.split(':')[0]\n };\n\n if (parsed.auth.split(':')[1]) {\n response.private_key = parsed.auth.split(':')[1];\n }\n\n if (~response.protocol.indexOf('+')) {\n response.protocol = response.protocol.split('+')[1];\n }\n\n if (!transports.hasOwnProperty(response.protocol)) {\n throw new Error('Invalid transport');\n }\n\n var index = parsed.pathname.lastIndexOf('/');\n response.path = parsed.pathname.substr(0, index + 1);\n response.project_id = parsed.pathname.substr(index + 1);\n response.port = ~~parsed.port || protocolMap[response.protocol] || 443;\n return response;\n } catch (e) {\n throw new Error('Invalid Sentry DSN: ' + dsn);\n }\n};\n\nmodule.exports.getCulprit = function getCulprit(frame) {\n if (frame.module || frame.function) {\n return (frame.module || '?') + ' at ' + (frame.function || '?');\n }\n return '';\n};\n\nvar moduleCache;\nmodule.exports.getModules = function getModules() {\n if (!moduleCache) {\n moduleCache = lsmod();\n }\n return moduleCache;\n};\n\nmodule.exports.fill = function(obj, name, replacement, track) {\n var orig = obj[name];\n obj[name] = replacement(orig);\n if (track) {\n track.push([obj, name, orig]);\n }\n};\n\nvar LINES_OF_CONTEXT = 7;\n\nfunction getFunction(line) {\n try {\n return (\n line.getFunctionName() ||\n line.getTypeName() + '.' + (line.getMethodName() || '')\n );\n } catch (e) {\n // This seems to happen sometimes when using 'use strict',\n // stemming from `getTypeName`.\n // [TypeError: Cannot read property 'constructor' of undefined]\n return '';\n }\n}\n\nvar mainModule =\n ((require.main && require.main.filename && path.dirname(require.main.filename)) ||\n process.cwd()) + '/';\n\nfunction getModule(filename, base) {\n if (!base) base = mainModule;\n\n // It's specifically a module\n var file = path.basename(filename, '.js');\n filename = path.dirname(filename);\n var n = filename.lastIndexOf('/node_modules/');\n if (n > -1) {\n // /node_modules/ is 14 chars\n return filename.substr(n + 14).replace(/\\//g, '.') + ':' + file;\n }\n // Let's see if it's a part of the main module\n // To be a part of main module, it has to share the same base\n n = (filename + '/').lastIndexOf(base, 0);\n if (n === 0) {\n var module = filename.substr(base.length).replace(/\\//g, '.');\n if (module) module += ':';\n module += file;\n return module;\n }\n return file;\n}\n\nfunction readSourceFiles(filenames, cb) {\n // we're relying on filenames being de-duped already\n if (filenames.length === 0) return setTimeout(cb, 0, {});\n\n var sourceFiles = {};\n var numFilesToRead = filenames.length;\n return filenames.forEach(function(filename) {\n fs.readFile(filename, function(readErr, file) {\n if (!readErr) sourceFiles[filename] = file.toString().split('\\n');\n if (--numFilesToRead === 0) cb(sourceFiles);\n });\n });\n}\n\n// This is basically just `trim_line` from https://github.com/getsentry/sentry/blob/master/src/sentry/lang/javascript/processor.py#L67\nfunction snipLine(line, colno) {\n var ll = line.length;\n if (ll <= 150) return line;\n if (colno > ll) colno = ll;\n\n var start = Math.max(colno - 60, 0);\n if (start < 5) start = 0;\n\n var end = Math.min(start + 140, ll);\n if (end > ll - 5) end = ll;\n if (end === ll) start = Math.max(end - 140, 0);\n\n line = line.slice(start, end);\n if (start > 0) line = '{snip} ' + line;\n if (end < ll) line += ' {snip}';\n\n return line;\n}\n\nfunction snipLine0(line) {\n return snipLine(line, 0);\n}\n\nfunction parseStack(err, cb) {\n if (!err) return cb([]);\n\n var stack = stacktrace.parse(err);\n if (!stack || !Array.isArray(stack) || !stack.length || !stack[0].getFileName) {\n // the stack is not the useful thing we were expecting :/\n return cb([]);\n }\n\n // Sentry expects the stack trace to be oldest -> newest, v8 provides newest -> oldest\n stack.reverse();\n\n var frames = [];\n var filesToRead = {};\n stack.forEach(function(line) {\n var frame = {\n filename: line.getFileName() || '',\n lineno: line.getLineNumber(),\n colno: line.getColumnNumber(),\n function: getFunction(line)\n };\n\n var isInternal =\n line.isNative() ||\n (frame.filename[0] !== '/' &&\n frame.filename[0] !== '.' &&\n frame.filename.indexOf(':\\\\') !== 1);\n\n // in_app is all that's not an internal Node function or a module within node_modules\n // note that isNative appears to return true even for node core libraries\n // see https://github.com/getsentry/raven-node/issues/176\n frame.in_app = !isInternal && frame.filename.indexOf('node_modules/') === -1;\n\n // Extract a module name based on the filename\n if (frame.filename) {\n frame.module = getModule(frame.filename);\n if (!isInternal) filesToRead[frame.filename] = true;\n }\n\n frames.push(frame);\n });\n\n return readSourceFiles(Object.keys(filesToRead), function(sourceFiles) {\n frames.forEach(function(frame) {\n if (frame.filename && sourceFiles[frame.filename]) {\n var lines = sourceFiles[frame.filename];\n try {\n frame.pre_context = lines\n .slice(Math.max(0, frame.lineno - (LINES_OF_CONTEXT + 1)), frame.lineno - 1)\n .map(snipLine0);\n frame.context_line = snipLine(lines[frame.lineno - 1], frame.colno);\n frame.post_context = lines\n .slice(frame.lineno, frame.lineno + LINES_OF_CONTEXT)\n .map(snipLine0);\n } catch (e) {\n // anomaly, being defensive in case\n // unlikely to ever happen in practice but can definitely happen in theory\n }\n }\n });\n\n cb(frames);\n });\n}\n\n// expose basically for testing because I don't know what I'm doing\nmodule.exports.parseStack = parseStack;\nmodule.exports.getModule = getModule;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/raven/lib/utils.js\n// module id = 0\n// module chunks = 0","module.exports = require(\"util\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"util\"\n// module id = 1\n// module chunks = 0","module.exports = require(\"path\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"path\"\n// module id = 2\n// module chunks = 0","'use strict';\n\nvar events = require('events');\nvar util = require('util');\nvar timeoutReq = require('timed-out');\n\nvar http = require('http');\nvar https = require('https');\n\nvar agentOptions = {keepAlive: true, maxSockets: 100};\nvar httpAgent = new http.Agent(agentOptions);\nvar httpsAgent = new https.Agent(agentOptions);\n\nfunction Transport() {}\nutil.inherits(Transport, events.EventEmitter);\n\nfunction HTTPTransport(options) {\n this.defaultPort = 80;\n this.transport = http;\n this.options = options || {};\n this.agent = httpAgent;\n}\nutil.inherits(HTTPTransport, Transport);\nHTTPTransport.prototype.send = function(client, message, headers, eventId, cb) {\n var options = {\n hostname: client.dsn.host,\n path: client.dsn.path + 'api/' + client.dsn.project_id + '/store/',\n headers: headers,\n method: 'POST',\n port: client.dsn.port || this.defaultPort,\n ca: client.ca,\n agent: this.agent\n };\n for (var key in this.options) {\n if (this.options.hasOwnProperty(key)) {\n options[key] = this.options[key];\n }\n }\n\n // prevent off heap memory explosion\n var _name = this.agent.getName({host: client.dsn.host, port: client.dsn.port});\n var _requests = this.agent.requests[_name];\n if (_requests && Object.keys(_requests).length > client.maxReqQueueCount) {\n // other feedback strategy\n client.emit('error', new Error('client req queue is full..'));\n return;\n }\n\n var req = this.transport.request(options, function(res) {\n res.setEncoding('utf8');\n if (res.statusCode >= 200 && res.statusCode < 300) {\n client.emit('logged', eventId);\n cb && cb(null, eventId);\n } else {\n var reason = res.headers['x-sentry-error'];\n var e = new Error('HTTP Error (' + res.statusCode + '): ' + reason);\n e.response = res;\n e.statusCode = res.statusCode;\n e.reason = reason;\n e.sendMessage = message;\n e.requestHeaders = headers;\n e.eventId = eventId;\n client.emit('error', e);\n cb && cb(e);\n }\n\n // force the socket to drain\n var noop = function() {};\n res.on('data', noop);\n res.on('end', noop);\n });\n\n timeoutReq(req, client.sendTimeout * 1000);\n\n var cbFired = false;\n req.on('error', function(e) {\n client.emit('error', e);\n if (!cbFired) {\n cb && cb(e);\n cbFired = true;\n }\n });\n req.end(message);\n};\n\nfunction HTTPSTransport(options) {\n this.defaultPort = 443;\n this.transport = https;\n this.options = options || {};\n this.agent = httpsAgent;\n}\nutil.inherits(HTTPSTransport, HTTPTransport);\n\nmodule.exports.http = new HTTPTransport();\nmodule.exports.https = new HTTPSTransport();\nmodule.exports.Transport = Transport;\nmodule.exports.HTTPTransport = HTTPTransport;\nmodule.exports.HTTPSTransport = HTTPSTransport;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/raven/lib/transports.js\n// module id = 3\n// module chunks = 0","'use strict';\n\nvar utils = require('../utils');\n\nvar defaultOnConfig = {\n console: true\n};\n\nvar defaultConfig = {\n console: false,\n http: false,\n pg: false\n};\n\nfunction instrument(Raven, config) {\n if (config === false) {\n return;\n } else if (config === true) {\n config = defaultOnConfig;\n } else {\n config = utils.extend({}, defaultConfig, config);\n }\n\n Raven.instrumentedOriginals = [];\n Raven.instrumentedModules = [];\n\n var Module = require('module');\n utils.fill(\n Module,\n '_load',\n function(origLoad) {\n return function(moduleId, parent, isMain) {\n var origModule = origLoad.apply(this, arguments);\n if (config[moduleId] && Raven.instrumentedModules.indexOf(moduleId) === -1) {\n Raven.instrumentedModules.push(moduleId);\n return require('./' + moduleId)(Raven, origModule, Raven.instrumentedOriginals);\n }\n return origModule;\n };\n },\n Raven.instrumentedOriginals\n );\n\n // special case: since console is built-in and app-level code won't require() it, do that here\n if (config.console) {\n require('console');\n }\n\n // observation: when the https module does its own require('http'), it *does not* hit our hooked require to instrument http on the fly\n // but if we've previously instrumented http, https *does* get our already-instrumented version\n // this is because raven's transports are required before this instrumentation takes place, which loads https (and http)\n // so module cache will have uninstrumented http; proactively loading it here ensures instrumented version is in module cache\n // alternatively we could refactor to load our transports later, but this is easier and doesn't have much drawback\n if (config.http) {\n require('http');\n }\n}\n\nfunction deinstrument(Raven) {\n if (!Raven.instrumentedOriginals) return;\n var original;\n // eslint-disable-next-line no-cond-assign\n while ((original = Raven.instrumentedOriginals.shift())) {\n var obj = original[0];\n var name = original[1];\n var orig = original[2];\n obj[name] = orig;\n }\n}\n\nmodule.exports = {\n instrument: instrument,\n deinstrument: deinstrument\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/raven/lib/instrumentation/instrumentor.js\n// module id = 4\n// module chunks = 0","'use strict';\n\n/*\n json-stringify-safe\n Like JSON.stringify, but doesn't throw on circular references.\n\n Originally forked from https://github.com/isaacs/json-stringify-safe\n version 5.0.1 on 2017-09-21 and modified to handle Errors serialization.\n Tests for this are in test/vendor.\n\n ISC license: https://github.com/isaacs/json-stringify-safe/blob/master/LICENSE\n */\n\nexports = module.exports = stringify;\nexports.getSerialize = serializer;\n\nfunction stringify(obj, replacer, spaces, cycleReplacer) {\n return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces);\n}\n\n// https://github.com/ftlabs/js-abbreviate/blob/fa709e5f139e7770a71827b1893f22418097fbda/index.js#L95-L106\nfunction stringifyError(value) {\n var err = {\n // These properties are implemented as magical getters and don't show up in for in\n stack: value.stack,\n message: value.message,\n name: value.name\n };\n\n for (var i in value) {\n if (Object.prototype.hasOwnProperty.call(value, i)) {\n err[i] = value[i];\n }\n }\n\n return err;\n}\n\nfunction serializer(replacer, cycleReplacer) {\n var stack = [];\n var keys = [];\n\n if (cycleReplacer == null) {\n cycleReplacer = function(key, value) {\n if (stack[0] === value) {\n return '[Circular ~]';\n }\n return '[Circular ~.' + keys.slice(0, stack.indexOf(value)).join('.') + ']';\n };\n }\n\n return function(key, value) {\n if (stack.length > 0) {\n var thisPos = stack.indexOf(this);\n ~thisPos ? stack.splice(thisPos + 1) : stack.push(this);\n ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key);\n\n if (~stack.indexOf(value)) {\n value = cycleReplacer.call(this, key, value);\n }\n } else {\n stack.push(value);\n }\n\n return replacer == null\n ? value instanceof Error ? stringifyError(value) : value\n : replacer.call(this, key, value);\n };\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/raven/vendor/json-stringify-safe.js\n// module id = 5\n// module chunks = 0","'use strict';\n\nvar cookie = require('cookie');\nvar urlParser = require('url');\nvar stringify = require('../vendor/json-stringify-safe');\n\nvar utils = require('./utils');\n\nmodule.exports.parseText = function parseText(message, kwargs) {\n kwargs = kwargs || {};\n kwargs.message = message;\n\n return kwargs;\n};\n\nmodule.exports.parseError = function parseError(err, kwargs, cb) {\n utils.parseStack(err, function(frames) {\n var name =\n ({}.hasOwnProperty.call(err, 'name') ? err.name : err.constructor.name) + '';\n if (typeof kwargs.message === 'undefined') {\n kwargs.message = name + ': ' + (err.message || '');\n }\n kwargs.exception = [\n {\n type: name,\n value: err.message,\n stacktrace: {\n frames: frames\n }\n }\n ];\n\n // Save additional error properties to `extra` under the error type (e.g. `extra.AttributeError`)\n var extraErrorProps;\n for (var key in err) {\n if (err.hasOwnProperty(key)) {\n if (key !== 'name' && key !== 'message' && key !== 'stack' && key !== 'domain') {\n extraErrorProps = extraErrorProps || {};\n extraErrorProps[key] = err[key];\n }\n }\n }\n if (extraErrorProps) {\n kwargs.extra = kwargs.extra || {};\n kwargs.extra[name] = extraErrorProps;\n }\n\n for (var n = frames.length - 1; n >= 0; n--) {\n if (frames[n].in_app) {\n kwargs.culprit = kwargs.culprit || utils.getCulprit(frames[n]);\n break;\n }\n }\n\n cb(kwargs);\n });\n};\n\nmodule.exports.parseRequest = function parseRequest(req, parseUser) {\n var kwargs = {};\n\n // headers:\n // node, express: req.headers\n // koa: req.header\n var headers = req.headers || req.header || {};\n\n // method:\n // node, express, koa: req.method\n var method = req.method;\n\n // host:\n // express: req.hostname in > 4 and req.host in < 4\n // koa: req.host\n // node: req.headers.host\n var host = req.hostname || req.host || headers.host || '';\n\n // protocol:\n // node: \n // express, koa: req.protocol\n var protocol =\n req.protocol === 'https' || req.secure || (req.socket || {}).encrypted\n ? 'https'\n : 'http';\n\n // url (including path and query string):\n // node, express: req.originalUrl\n // koa: req.url\n var originalUrl = req.originalUrl || req.url;\n\n // absolute url\n var absoluteUrl = protocol + '://' + host + originalUrl;\n\n // query string:\n // node: req.url (raw)\n // express, koa: req.query\n var query = req.query || urlParser.parse(originalUrl || '', true).query;\n\n // cookies:\n // node, express, koa: req.headers.cookie\n var cookies = cookie.parse(headers.cookie || '');\n\n // body data:\n // node, express, koa: req.body\n var data = req.body;\n if (['GET', 'HEAD'].indexOf(method) === -1) {\n if (typeof data === 'undefined') {\n data = '';\n }\n }\n\n if (data && typeof data !== 'string' && {}.toString.call(data) !== '[object String]') {\n // Make sure the request body is a string\n data = stringify(data);\n }\n\n // http interface\n var http = {\n method: method,\n query_string: query,\n headers: headers,\n cookies: cookies,\n data: data,\n url: absoluteUrl\n };\n\n // expose http interface\n kwargs.request = http;\n\n // user: typically found on req.user in express/passport patterns\n // five cases for parseUser value:\n // absent: grab only id, username, email from req.user\n // false: capture nothing\n // true: capture all keys from req.user\n // array: provided whitelisted keys to grab from req.user\n // function :: req -> user: custom parsing function\n if (parseUser == null) parseUser = ['id', 'username', 'email'];\n if (parseUser) {\n var user = {};\n if (typeof parseUser === 'function') {\n user = parseUser(req);\n } else if (req.user) {\n if (parseUser === true) {\n for (var key in req.user) {\n if ({}.hasOwnProperty.call(req.user, key)) {\n user[key] = req.user[key];\n }\n }\n } else {\n parseUser.forEach(function(fieldName) {\n if ({}.hasOwnProperty.call(req.user, fieldName)) {\n user[fieldName] = req.user[fieldName];\n }\n });\n }\n }\n\n // client ip:\n // node: req.connection.remoteAddress\n // express, koa: req.ip\n var ip = req.ip || (req.connection && req.connection.remoteAddress);\n if (ip) {\n user.ip_address = ip;\n }\n\n kwargs.user = user;\n }\n\n return kwargs;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/raven/lib/parsers.js\n// module id = 6\n// module chunks = 0","module.exports = require(\"url\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"url\"\n// module id = 7\n// module chunks = 0","module.exports = require(\"fs\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"fs\"\n// module id = 8\n// module chunks = 0","module.exports = require(\"events\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"events\"\n// module id = 9\n// module chunks = 0","module.exports = require(\"http\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"http\"\n// module id = 10\n// module chunks = 0","module.exports = {\"_from\":\"raven@^2.2.1\",\"_id\":\"raven@2.2.1\",\"_inBundle\":false,\"_integrity\":\"sha1-V8f75oqAFH7FJ97z18AVdc+Uj+M=\",\"_location\":\"/raven\",\"_phantomChildren\":{},\"_requested\":{\"type\":\"range\",\"registry\":true,\"raw\":\"raven@^2.2.1\",\"name\":\"raven\",\"escapedName\":\"raven\",\"rawSpec\":\"^2.2.1\",\"saveSpec\":null,\"fetchSpec\":\"^2.2.1\"},\"_requiredBy\":[\"#USER\",\"/\"],\"_resolved\":\"https://registry.npmjs.org/raven/-/raven-2.2.1.tgz\",\"_shasum\":\"57c7fbe68a80147ec527def3d7c01575cf948fe3\",\"_spec\":\"raven@^2.2.1\",\"_where\":\"/Users/kamilogorek/Projects/sentry/repros/node-sourcemaps\",\"author\":{\"name\":\"Matt Robenolt\",\"email\":\"matt@ydekproductions.com\"},\"bin\":{\"raven\":\"./bin/raven\"},\"bugs\":{\"url\":\"https://github.com/getsentry/raven-node/issues\"},\"bundleDependencies\":false,\"dependencies\":{\"cookie\":\"0.3.1\",\"lsmod\":\"1.0.0\",\"stack-trace\":\"0.0.9\",\"timed-out\":\"4.0.1\",\"uuid\":\"3.0.0\"},\"deprecated\":false,\"description\":\"A standalone (Node.js) client for Sentry\",\"devDependencies\":{\"coffee-script\":\"~1.10.0\",\"connect\":\"*\",\"eslint\":\"^4.5.0\",\"eslint-config-prettier\":\"^2.3.0\",\"express\":\"*\",\"glob\":\"~3.1.13\",\"husky\":\"^0.14.3\",\"istanbul\":\"^0.4.3\",\"lint-staged\":\"^4.0.4\",\"mocha\":\"~3.1.2\",\"nock\":\"~9.0.0\",\"prettier\":\"^1.6.1\",\"should\":\"11.2.0\",\"sinon\":\"^3.3.0\"},\"engines\":{\"node\":\">= 4.0.0\"},\"homepage\":\"https://github.com/getsentry/raven-node\",\"keywords\":[\"debugging\",\"errors\",\"exceptions\",\"logging\",\"raven\",\"sentry\"],\"license\":\"BSD-2-Clause\",\"lint-staged\":{\"*.js\":[\"prettier --write\",\"git add\"]},\"main\":\"index.js\",\"name\":\"raven\",\"prettier\":{\"singleQuote\":true,\"bracketSpacing\":false,\"printWidth\":90},\"repository\":{\"type\":\"git\",\"url\":\"git://github.com/getsentry/raven-node.git\"},\"scripts\":{\"lint\":\"node_modules/eslint/bin/eslint.js .\",\"precommit\":\"lint-staged\",\"pretest\":\"npm install && npm run lint\",\"test\":\"NODE_ENV=test istanbul cover _mocha -- --reporter dot && NODE_ENV=test node_modules/coffee-script/bin/coffee ./test/run.coffee\",\"test-full\":\"npm run test && cd test/instrumentation && ./run.sh\",\"test-mocha\":\"NODE_ENV=test mocha\"},\"version\":\"2.2.1\"}\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/raven/package.json\n// module id = 11\n// module chunks = 0","'use strict';\n\nvar util = require('util');\nvar utils = require('../utils');\n\nmodule.exports = function(Raven, console, originals) {\n var wrapConsoleMethod = function(level) {\n if (!(level in console)) {\n return;\n }\n\n utils.fill(\n console,\n level,\n function(originalConsoleLevel) {\n var sentryLevel = level === 'warn' ? 'warning' : level;\n\n return function() {\n var args = [].slice.call(arguments);\n\n Raven.captureBreadcrumb({\n message: util.format.apply(null, args),\n level: sentryLevel,\n category: 'console'\n });\n\n originalConsoleLevel.apply(console, args);\n };\n },\n originals\n );\n };\n\n ['debug', 'info', 'warn', 'error', 'log'].forEach(wrapConsoleMethod);\n\n return console;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/raven/lib/instrumentation/console.js\n// module id = 12\n// module chunks = 0","'use strict';\nvar util = require('util');\nvar utils = require('../utils');\n\nmodule.exports = function(Raven, http, originals) {\n var OrigClientRequest = http.ClientRequest;\n var ClientRequest = function(options, cb) {\n // Note: this won't capture a breadcrumb if a response never comes\n // It would be useful to know if that was the case, though, so\n // todo: revisit to see if we can capture sth indicating response never came\n // possibility: capture one breadcrumb for \"req sent\" and one for \"res recvd\"\n // seems excessive but solves the problem and *is* strictly more information\n // could be useful for weird response sequencing bug scenarios\n OrigClientRequest.call(this, options, cb);\n\n // We could just always reconstruct this from this.agent, this._headers, this.path, etc\n // but certain other http-instrumenting libraries (like nock, which we use for tests) fail to\n // maintain the guarantee that after calling OrigClientRequest, those fields will be populated\n if (typeof options === 'string') {\n this.__ravenBreadcrumbUrl = options;\n } else {\n this.__ravenBreadcrumbUrl =\n (options.protocol || '') +\n '//' +\n (options.hostname || options.host || '') +\n (options.path || '/');\n }\n };\n util.inherits(ClientRequest, OrigClientRequest);\n\n utils.fill(ClientRequest.prototype, 'emit', function(origEmit) {\n return function(evt, maybeResp) {\n if (evt === 'response' && this.__ravenBreadcrumbUrl) {\n if (!Raven.dsn || this.__ravenBreadcrumbUrl.indexOf(Raven.dsn.host) === -1) {\n Raven.captureBreadcrumb({\n type: 'http',\n category: 'http',\n data: {\n method: this.method,\n url: this.__ravenBreadcrumbUrl,\n status_code: maybeResp.statusCode\n }\n });\n }\n }\n return origEmit.apply(this, arguments);\n };\n });\n\n utils.fill(\n http,\n 'ClientRequest',\n function() {\n return ClientRequest;\n },\n originals\n );\n\n // http.request orig refs module-internal ClientRequest, not exported one, so\n // it still points at orig ClientRequest after our monkeypatch; these reimpls\n // just get that reference updated to use our new ClientRequest\n utils.fill(\n http,\n 'request',\n function() {\n return function(options, cb) {\n return new http.ClientRequest(options, cb);\n };\n },\n originals\n );\n\n utils.fill(\n http,\n 'get',\n function() {\n return function(options, cb) {\n var req = http.request(options, cb);\n req.end();\n return req;\n };\n },\n originals\n );\n\n return http;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/raven/lib/instrumentation/http.js\n// module id = 13\n// module chunks = 0","'use strict';\nmodule.exports = function(Raven, pg, originals) {\n // Using fill helper here is hard because of `this` binding\n var origQuery = pg.Connection.prototype.query;\n pg.Connection.prototype.query = function(text) {\n Raven.captureBreadcrumb({\n category: 'postgres',\n message: text\n });\n origQuery.call(this, text);\n };\n // todo thread this through\n // originals.push([pg.Connection.prototype, 'query', origQuery]);\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/raven/lib/instrumentation/pg.js\n// module id = 14\n// module chunks = 0","var Raven = require('raven');\nvar path = require('path');\nvar foo = require('./src/foo.js');\n\nRaven.config(\n 'http://36dfaa7c54664f429aac79ac89d7fb68:b4505a72a8ce4ecd8deb8038124b0909@localhost:8000/8',\n {\n release: process.env.RELEASE,\n dataCallback: function(data) {\n var stacktrace = data.exception && data.exception[0].stacktrace;\n\n if (stacktrace && stacktrace.frames) {\n stacktrace.frames.forEach(function(frame) {\n if (frame.filename.startsWith('/')) {\n frame.filename = 'app:///' + path.basename(frame.filename);\n }\n });\n }\n\n\t\t\tconsole.log(JSON.stringify(data, null, 2))\n\n return data;\n },\n shouldSendCallback: function() {\n return 'false';\n },\n },\n).install();\n\nfunction App() {\n foo();\n}\n\nApp();\n\nsetTimeout(function() {\n App();\n}, 500);\n\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./index.js\n// module id = 15\n// module chunks = 0","'use strict';\n\nmodule.exports = require('./lib/client');\nmodule.exports.utils = require('./lib/utils');\n\nmodule.exports.transports = require('./lib/transports');\nmodule.exports.parsers = require('./lib/parsers');\n\n// To infinity and beyond\nError.stackTraceLimit = Infinity;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/raven/index.js\n// module id = 16\n// module chunks = 0","'use strict';\n\nvar stringify = require('../vendor/json-stringify-safe');\nvar parsers = require('./parsers');\nvar zlib = require('zlib');\nvar utils = require('./utils');\nvar uuid = require('uuid');\nvar transports = require('./transports');\nvar nodeUtil = require('util'); // nodeUtil to avoid confusion with \"utils\"\nvar events = require('events');\nvar domain = require('domain');\n\nvar instrumentor = require('./instrumentation/instrumentor');\n\nvar extend = utils.extend;\n\nfunction Raven() {\n this.breadcrumbs = {\n record: this.captureBreadcrumb.bind(this)\n };\n}\n\nnodeUtil.inherits(Raven, events.EventEmitter);\n\nextend(Raven.prototype, {\n config: function config(dsn, options) {\n // We get lots of users using raven-node when they want raven-js, hence this warning if it seems like a browser\n if (\n typeof window !== 'undefined' &&\n typeof document !== 'undefined' &&\n typeof navigator !== 'undefined'\n ) {\n utils.consoleAlertOnce(\n \"This looks like a browser environment; are you sure you don't want Raven.js for browser JavaScript? https://sentry.io/for/javascript\"\n );\n }\n\n if (arguments.length === 0) {\n // no arguments, use default from environment\n dsn = process.env.SENTRY_DSN;\n options = {};\n }\n if (typeof dsn === 'object') {\n // They must only be passing through options\n options = dsn;\n dsn = process.env.SENTRY_DSN;\n }\n options = options || {};\n\n this.raw_dsn = dsn;\n this.dsn = utils.parseDSN(dsn);\n this.name = options.name || process.env.SENTRY_NAME || require('os').hostname();\n this.root = options.root || process.cwd();\n this.transport = options.transport || transports[this.dsn.protocol];\n this.sendTimeout = options.sendTimeout || 1;\n this.release = options.release || process.env.SENTRY_RELEASE || '';\n this.environment =\n options.environment || process.env.SENTRY_ENVIRONMENT || process.env.NODE_ENV || '';\n\n // autoBreadcrumbs: true enables all, autoBreadcrumbs: false disables all\n // autoBreadcrumbs: { http: true } enables a single type\n this.autoBreadcrumbs = options.autoBreadcrumbs || false;\n // default to 30, don't allow higher than 100\n this.maxBreadcrumbs = Math.max(0, Math.min(options.maxBreadcrumbs || 30, 100));\n\n this.captureUnhandledRejections = options.captureUnhandledRejections;\n this.loggerName = options.logger || '';\n this.dataCallback = options.dataCallback;\n this.shouldSendCallback = options.shouldSendCallback;\n this.sampleRate = typeof options.sampleRate === 'undefined' ? 1 : options.sampleRate;\n this.maxReqQueueCount = options.maxReqQueueCount || 100;\n this.parseUser = options.parseUser;\n\n if (!this.dsn) {\n utils.consoleAlert('no DSN provided, error reporting disabled');\n }\n\n if (this.dsn.protocol === 'https') {\n // In case we want to provide our own SSL certificates / keys\n this.ca = options.ca || null;\n }\n\n // enabled if a dsn is set\n this._enabled = !!this.dsn;\n\n var globalContext = (this._globalContext = {});\n if (options.tags) {\n globalContext.tags = options.tags;\n }\n if (options.extra) {\n globalContext.extra = options.extra;\n }\n\n this.onFatalError = this.defaultOnFatalError = function(err, sendErr, eventId) {\n console.error(err && err.stack ? err.stack : err);\n process.exit(1);\n };\n this.uncaughtErrorHandler = this.makeErrorHandler();\n\n this.on('error', function(err) {\n utils.consoleAlert('failed to send exception to sentry: ' + err.message);\n });\n\n return this;\n },\n\n install: function install(cb) {\n if (this.installed) return this;\n\n if (typeof cb === 'function') {\n this.onFatalError = cb;\n }\n\n process.on('uncaughtException', this.uncaughtErrorHandler);\n\n if (this.captureUnhandledRejections) {\n var self = this;\n process.on('unhandledRejection', function(reason) {\n self.captureException(reason, function(sendErr, eventId) {\n if (!sendErr) utils.consoleAlert('unhandledRejection captured: ' + eventId);\n });\n });\n }\n\n instrumentor.instrument(this, this.autoBreadcrumbs);\n\n this.installed = true;\n\n return this;\n },\n\n uninstall: function uninstall() {\n if (!this.installed) return this;\n\n instrumentor.deinstrument(this);\n\n // todo: this works for tests for now, but isn't what we ultimately want to be doing\n process.removeAllListeners('uncaughtException');\n process.removeAllListeners('unhandledRejection');\n\n this.installed = false;\n\n return this;\n },\n\n makeErrorHandler: function() {\n var self = this;\n var caughtFirstError = false;\n var caughtSecondError = false;\n var calledFatalError = false;\n var firstError;\n return function(err) {\n if (!caughtFirstError) {\n // this is the first uncaught error and the ultimate reason for shutting down\n // we want to do absolutely everything possible to ensure it gets captured\n // also we want to make sure we don't go recursion crazy if more errors happen after this one\n firstError = err;\n caughtFirstError = true;\n self.captureException(err, function(sendErr, eventId) {\n if (!calledFatalError) {\n calledFatalError = true;\n self.onFatalError(err, sendErr, eventId);\n }\n });\n } else if (calledFatalError) {\n // we hit an error *after* calling onFatalError - pretty boned at this point, just shut it down\n utils.consoleAlert(\n 'uncaught exception after calling fatal error shutdown callback - this is bad! forcing shutdown'\n );\n self.defaultOnFatalError(err);\n } else if (!caughtSecondError) {\n // two cases for how we can hit this branch:\n // - capturing of first error blew up and we just caught the exception from that\n // - quit trying to capture, proceed with shutdown\n // - a second independent error happened while waiting for first error to capture\n // - want to avoid causing premature shutdown before first error capture finishes\n // it's hard to immediately tell case 1 from case 2 without doing some fancy/questionable domain stuff\n // so let's instead just delay a bit before we proceed with our action here\n // in case 1, we just wait a bit unnecessarily but ultimately do the same thing\n // in case 2, the delay hopefully made us wait long enough for the capture to finish\n // two potential nonideal outcomes:\n // nonideal case 1: capturing fails fast, we sit around for a few seconds unnecessarily before proceeding correctly by calling onFatalError\n // nonideal case 2: case 2 happens, 1st error is captured but slowly, timeout completes before capture and we treat second error as the sendErr of (nonexistent) failure from trying to capture first error\n // note that after hitting this branch, we might catch more errors where (caughtSecondError && !calledFatalError)\n // we ignore them - they don't matter to us, we're just waiting for the second error timeout to finish\n caughtSecondError = true;\n setTimeout(function() {\n if (!calledFatalError) {\n // it was probably case 1, let's treat err as the sendErr and call onFatalError\n calledFatalError = true;\n self.onFatalError(firstError, err);\n } else {\n // it was probably case 2, our first error finished capturing while we waited, cool, do nothing\n }\n }, (self.sendTimeout + 1) * 1000); // capturing could take at least sendTimeout to fail, plus an arbitrary second for how long it takes to collect surrounding source etc\n }\n };\n },\n\n generateEventId: function generateEventId() {\n return uuid().replace(/-/g, '');\n },\n\n process: function process(eventId, kwargs, cb) {\n // prod codepaths shouldn't hit this branch, for testing\n if (typeof eventId === 'object') {\n cb = kwargs;\n kwargs = eventId;\n eventId = this.generateEventId();\n }\n\n var domainContext = (domain.active && domain.active.sentryContext) || {};\n kwargs.user = extend({}, this._globalContext.user, domainContext.user, kwargs.user);\n kwargs.tags = extend({}, this._globalContext.tags, domainContext.tags, kwargs.tags);\n kwargs.extra = extend(\n {},\n this._globalContext.extra,\n domainContext.extra,\n kwargs.extra\n );\n kwargs.breadcrumbs = {\n values: domainContext.breadcrumbs || this._globalContext.breadcrumbs || []\n };\n\n /*\n `request` is our specified property name for the http interface: https://docs.sentry.io/clientdev/interfaces/http/\n `req` is the conventional name for a request object in node/express/etc\n we want to enable someone to pass a `request` property to kwargs according to http interface\n but also want to provide convenience for passing a req object and having us parse it out\n so we only parse a `req` property if the `request` property is absent/empty (and hence we won't clobber)\n parseUser returns a partial kwargs object with a `request` property and possibly a `user` property\n */\n kwargs.request = this._createRequestObject(\n this._globalContext.request,\n domainContext.request,\n kwargs.request\n );\n if (Object.keys(kwargs.request).length === 0) {\n var req = this._createRequestObject(\n this._globalContext.req,\n domainContext.req,\n kwargs.req\n );\n if (Object.keys(req).length > 0) {\n var parseUser = Object.keys(kwargs.user).length === 0 ? this.parseUser : false;\n extend(kwargs, parsers.parseRequest(req, parseUser));\n delete kwargs.req;\n }\n }\n\n kwargs.modules = utils.getModules();\n kwargs.server_name = kwargs.server_name || this.name;\n\n if (typeof process.version !== 'undefined') {\n kwargs.extra.node = process.version;\n }\n\n kwargs.environment = kwargs.environment || this.environment;\n kwargs.logger = kwargs.logger || this.loggerName;\n kwargs.event_id = eventId;\n kwargs.timestamp = new Date().toISOString().split('.')[0];\n kwargs.project = this.dsn.project_id;\n kwargs.platform = 'node';\n\n // Only include release information if it is set\n if (this.release) {\n kwargs.release = this.release;\n }\n\n if (this.dataCallback) {\n kwargs = this.dataCallback(kwargs);\n }\n\n var shouldSend = true;\n if (!this._enabled) shouldSend = false;\n if (this.shouldSendCallback && !this.shouldSendCallback(kwargs)) shouldSend = false;\n if (Math.random() >= this.sampleRate) shouldSend = false;\n\n if (shouldSend) {\n this.send(kwargs, cb);\n } else {\n // wish there was a good way to communicate to cb why we didn't send; worth considering cb api change?\n // could be shouldSendCallback, could be disabled, could be sample rate\n // avoiding setImmediate here because node 0.8\n cb &&\n setTimeout(function() {\n cb(null, eventId);\n }, 0);\n }\n },\n\n send: function send(kwargs, cb) {\n var self = this;\n var skwargs = stringify(kwargs);\n var eventId = kwargs.event_id;\n\n zlib.deflate(skwargs, function(err, buff) {\n var message = buff.toString('base64'),\n timestamp = new Date().getTime(),\n headers = {\n 'X-Sentry-Auth': utils.getAuthHeader(\n timestamp,\n self.dsn.public_key,\n self.dsn.private_key\n ),\n 'Content-Type': 'application/octet-stream',\n 'Content-Length': message.length\n };\n\n self.transport.send(self, message, headers, eventId, cb);\n });\n },\n\n captureMessage: function captureMessage(message, kwargs, cb) {\n if (!cb && typeof kwargs === 'function') {\n cb = kwargs;\n kwargs = {};\n } else {\n kwargs = kwargs || {};\n }\n var eventId = this.generateEventId();\n this.process(eventId, parsers.parseText(message, kwargs), cb);\n\n return eventId;\n },\n\n captureException: function captureException(err, kwargs, cb) {\n if (!(err instanceof Error)) {\n // This handles when someone does:\n // throw \"something awesome\";\n // We synthesize an Error here so we can extract a (rough) stack trace.\n err = new Error(err);\n }\n\n if (!cb && typeof kwargs === 'function') {\n cb = kwargs;\n kwargs = {};\n } else {\n kwargs = kwargs || {};\n }\n\n var self = this;\n var eventId = this.generateEventId();\n parsers.parseError(err, kwargs, function(kw) {\n self.process(eventId, kw, cb);\n });\n\n return eventId;\n },\n\n context: function(ctx, func) {\n if (!func && typeof ctx === 'function') {\n func = ctx;\n ctx = {};\n }\n\n // todo/note: raven-js takes an args param to do apply(this, args)\n // i don't think it's correct/necessary to bind this to the wrap call\n // and i don't know if we need to support the args param; it's undocumented\n return this.wrap(ctx, func).apply(null);\n },\n\n wrap: function(options, func) {\n if (!func && typeof options === 'function') {\n func = options;\n options = {};\n }\n\n var wrapDomain = domain.create();\n // todo: better property name than sentryContext, maybe __raven__ or sth?\n wrapDomain.sentryContext = options;\n\n wrapDomain.on('error', this.uncaughtErrorHandler);\n var wrapped = wrapDomain.bind(func);\n\n for (var property in func) {\n if ({}.hasOwnProperty.call(func, property)) {\n wrapped[property] = func[property];\n }\n }\n wrapped.prototype = func.prototype;\n wrapped.__raven__ = true;\n wrapped.__inner__ = func;\n // note: domain.bind sets wrapped.domain, but it's not documented, unsure if we should rely on that\n wrapped.__domain__ = wrapDomain;\n\n return wrapped;\n },\n\n interceptErr: function(options, func) {\n if (!func && typeof options === 'function') {\n func = options;\n options = {};\n }\n var self = this;\n var wrapped = function() {\n var err = arguments[0];\n if (err instanceof Error) {\n self.captureException(err, options);\n } else {\n func.apply(null, arguments);\n }\n };\n\n // repetitive with wrap\n for (var property in func) {\n if ({}.hasOwnProperty.call(func, property)) {\n wrapped[property] = func[property];\n }\n }\n wrapped.prototype = func.prototype;\n wrapped.__raven__ = true;\n wrapped.__inner__ = func;\n\n return wrapped;\n },\n\n setContext: function setContext(ctx) {\n if (domain.active) {\n domain.active.sentryContext = ctx;\n } else {\n this._globalContext = ctx;\n }\n return this;\n },\n\n mergeContext: function mergeContext(ctx) {\n extend(this.getContext(), ctx);\n return this;\n },\n\n getContext: function getContext() {\n if (domain.active) {\n if (!domain.active.sentryContext) {\n domain.active.sentryContext = {};\n utils.consoleAlert('sentry context not found on active domain');\n }\n return domain.active.sentryContext;\n }\n return this._globalContext;\n },\n\n setCallbackHelper: function(propertyName, callback) {\n var original = this[propertyName];\n if (typeof callback === 'function') {\n this[propertyName] = function(data) {\n return callback(data, original);\n };\n } else {\n this[propertyName] = callback;\n }\n\n return this;\n },\n\n /*\n * Set the dataCallback option\n *\n * @param {function} callback The callback to run which allows the\n * data blob to be mutated before sending\n * @return {Raven}\n */\n setDataCallback: function(callback) {\n return this.setCallbackHelper('dataCallback', callback);\n },\n\n /*\n * Set the shouldSendCallback option\n *\n * @param {function} callback The callback to run which allows\n * introspecting the blob before sending\n * @return {Raven}\n */\n setShouldSendCallback: function(callback) {\n return this.setCallbackHelper('shouldSendCallback', callback);\n },\n\n requestHandler: function() {\n var self = this;\n return function(req, res, next) {\n self.context({req: req}, function() {\n domain.active.add(req);\n domain.active.add(res);\n next();\n });\n };\n },\n\n errorHandler: function() {\n var self = this;\n return function(err, req, res, next) {\n var status = err.status || err.statusCode || err.status_code || 500;\n\n // skip anything not marked as an internal server error\n if (status < 500) return next(err);\n\n var eventId = self.captureException(err, {req: req});\n res.sentry = eventId;\n return next(err);\n };\n },\n\n captureBreadcrumb: function(breadcrumb) {\n // Avoid capturing global-scoped breadcrumbs before instrumentation finishes\n if (!this.installed) return;\n\n breadcrumb = extend(\n {\n timestamp: +new Date() / 1000\n },\n breadcrumb\n );\n var currCtx = this.getContext();\n if (!currCtx.breadcrumbs) currCtx.breadcrumbs = [];\n currCtx.breadcrumbs.push(breadcrumb);\n if (currCtx.breadcrumbs.length > this.maxBreadcrumbs) {\n currCtx.breadcrumbs.shift();\n }\n this.setContext(currCtx);\n },\n\n _createRequestObject: function() {\n /**\n * When using proxy, some of the attributes of req/request objects are non-enumerable.\n * To make sure, that they are still available to us after we consolidate our sources\n * (eg. globalContext.request + domainContext.request + kwargs.request),\n * we manually pull them out from original objects.\n *\n * We don't use Object.assign/extend as it's only merging over objects own properties,\n * and we don't want to go through all of the properties as well, as we simply don't\n * need all of them.\n *\n * So far the only missing piece is `ip`, but we can specify what properties should\n * be pulled by extending `nonEnumerables` array.\n **/\n var sources = Array.from(arguments).filter(function(source) {\n return Object.prototype.toString.call(source) === '[object Object]';\n });\n sources = [{}].concat(sources);\n var request = extend.apply(null, sources);\n var nonEnumberables = ['ip'];\n\n nonEnumberables.forEach(function(key) {\n sources.forEach(function(source) {\n if (source[key]) request[key] = source[key];\n });\n });\n\n return request;\n }\n});\n\n// Maintain old API compat, need to make sure arguments length is preserved\nfunction Client(dsn, options) {\n if (dsn instanceof Client) return dsn;\n var ravenInstance = new Raven();\n return ravenInstance.config.apply(ravenInstance, arguments);\n}\nnodeUtil.inherits(Client, Raven);\n\n// Singleton-by-default but not strictly enforced\n// todo these extra export props are sort of an adhoc mess, better way to manage?\nvar defaultInstance = new Raven();\ndefaultInstance.Client = Client;\ndefaultInstance.version = require('../package.json').version;\ndefaultInstance.disableConsoleAlerts = utils.disableConsoleAlerts;\n\nmodule.exports = defaultInstance;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/raven/lib/client.js\n// module id = 17\n// module chunks = 0","/*!\n * cookie\n * Copyright(c) 2012-2014 Roman Shtylman\n * Copyright(c) 2015 Douglas Christopher Wilson\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module exports.\n * @public\n */\n\nexports.parse = parse;\nexports.serialize = serialize;\n\n/**\n * Module variables.\n * @private\n */\n\nvar decode = decodeURIComponent;\nvar encode = encodeURIComponent;\nvar pairSplitRegExp = /; */;\n\n/**\n * RegExp to match field-content in RFC 7230 sec 3.2\n *\n * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]\n * field-vchar = VCHAR / obs-text\n * obs-text = %x80-FF\n */\n\nvar fieldContentRegExp = /^[\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+$/;\n\n/**\n * Parse a cookie header.\n *\n * Parse the given cookie header string into an object\n * The object has the various cookies as keys(names) => values\n *\n * @param {string} str\n * @param {object} [options]\n * @return {object}\n * @public\n */\n\nfunction parse(str, options) {\n if (typeof str !== 'string') {\n throw new TypeError('argument str must be a string');\n }\n\n var obj = {}\n var opt = options || {};\n var pairs = str.split(pairSplitRegExp);\n var dec = opt.decode || decode;\n\n for (var i = 0; i < pairs.length; i++) {\n var pair = pairs[i];\n var eq_idx = pair.indexOf('=');\n\n // skip things that don't look like key=value\n if (eq_idx < 0) {\n continue;\n }\n\n var key = pair.substr(0, eq_idx).trim()\n var val = pair.substr(++eq_idx, pair.length).trim();\n\n // quoted values\n if ('\"' == val[0]) {\n val = val.slice(1, -1);\n }\n\n // only assign once\n if (undefined == obj[key]) {\n obj[key] = tryDecode(val, dec);\n }\n }\n\n return obj;\n}\n\n/**\n * Serialize data into a cookie header.\n *\n * Serialize the a name value pair into a cookie string suitable for\n * http headers. An optional options object specified cookie parameters.\n *\n * serialize('foo', 'bar', { httpOnly: true })\n * => \"foo=bar; httpOnly\"\n *\n * @param {string} name\n * @param {string} val\n * @param {object} [options]\n * @return {string}\n * @public\n */\n\nfunction serialize(name, val, options) {\n var opt = options || {};\n var enc = opt.encode || encode;\n\n if (typeof enc !== 'function') {\n throw new TypeError('option encode is invalid');\n }\n\n if (!fieldContentRegExp.test(name)) {\n throw new TypeError('argument name is invalid');\n }\n\n var value = enc(val);\n\n if (value && !fieldContentRegExp.test(value)) {\n throw new TypeError('argument val is invalid');\n }\n\n var str = name + '=' + value;\n\n if (null != opt.maxAge) {\n var maxAge = opt.maxAge - 0;\n if (isNaN(maxAge)) throw new Error('maxAge should be a Number');\n str += '; Max-Age=' + Math.floor(maxAge);\n }\n\n if (opt.domain) {\n if (!fieldContentRegExp.test(opt.domain)) {\n throw new TypeError('option domain is invalid');\n }\n\n str += '; Domain=' + opt.domain;\n }\n\n if (opt.path) {\n if (!fieldContentRegExp.test(opt.path)) {\n throw new TypeError('option path is invalid');\n }\n\n str += '; Path=' + opt.path;\n }\n\n if (opt.expires) {\n if (typeof opt.expires.toUTCString !== 'function') {\n throw new TypeError('option expires is invalid');\n }\n\n str += '; Expires=' + opt.expires.toUTCString();\n }\n\n if (opt.httpOnly) {\n str += '; HttpOnly';\n }\n\n if (opt.secure) {\n str += '; Secure';\n }\n\n if (opt.sameSite) {\n var sameSite = typeof opt.sameSite === 'string'\n ? opt.sameSite.toLowerCase() : opt.sameSite;\n\n switch (sameSite) {\n case true:\n str += '; SameSite=Strict';\n break;\n case 'lax':\n str += '; SameSite=Lax';\n break;\n case 'strict':\n str += '; SameSite=Strict';\n break;\n default:\n throw new TypeError('option sameSite is invalid');\n }\n }\n\n return str;\n}\n\n/**\n * Try decoding a string using a decoding function.\n *\n * @param {string} str\n * @param {function} decode\n * @private\n */\n\nfunction tryDecode(str, decode) {\n try {\n return decode(str);\n } catch (e) {\n return str;\n }\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/cookie/index.js\n// module id = 18\n// module chunks = 0","'use strict';\n\nmodule.exports = function (req, time) {\n\tif (req.timeoutTimer) {\n\t\treturn req;\n\t}\n\n\tvar delays = isNaN(time) ? time : {socket: time, connect: time};\n\tvar host = req._headers ? (' to ' + req._headers.host) : '';\n\n\tif (delays.connect !== undefined) {\n\t\treq.timeoutTimer = setTimeout(function timeoutHandler() {\n\t\t\treq.abort();\n\t\t\tvar e = new Error('Connection timed out on request' + host);\n\t\t\te.code = 'ETIMEDOUT';\n\t\t\treq.emit('error', e);\n\t\t}, delays.connect);\n\t}\n\n\t// Clear the connection timeout timer once a socket is assigned to the\n\t// request and is connected.\n\treq.on('socket', function assign(socket) {\n\t\t// Socket may come from Agent pool and may be already connected.\n\t\tif (!(socket.connecting || socket._connecting)) {\n\t\t\tconnect();\n\t\t\treturn;\n\t\t}\n\n\t\tsocket.once('connect', connect);\n\t});\n\n\tfunction clear() {\n\t\tif (req.timeoutTimer) {\n\t\t\tclearTimeout(req.timeoutTimer);\n\t\t\treq.timeoutTimer = null;\n\t\t}\n\t}\n\n\tfunction connect() {\n\t\tclear();\n\n\t\tif (delays.socket !== undefined) {\n\t\t\t// Abort the request if there is no activity on the socket for more\n\t\t\t// than `delays.socket` milliseconds.\n\t\t\treq.setTimeout(delays.socket, function socketTimeoutHandler() {\n\t\t\t\treq.abort();\n\t\t\t\tvar e = new Error('Socket timed out on request' + host);\n\t\t\t\te.code = 'ESOCKETTIMEDOUT';\n\t\t\t\treq.emit('error', e);\n\t\t\t});\n\t\t}\n\t}\n\n\treturn req.on('error', clear);\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/timed-out/index.js\n// module id = 19\n// module chunks = 0","module.exports = require(\"https\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"https\"\n// module id = 20\n// module chunks = 0","// builtin\nvar fs = require('fs');\nvar path = require('path');\n\n// node 0.6 support\nfs.existsSync = fs.existsSync || path.existsSync;\n\n// main_paths are the paths where our mainprog will be able to load from\n// we store these to avoid grabbing the modules that were loaded as a result\n// of a dependency module loading its dependencies, we only care about deps our\n// mainprog loads\nvar main_paths = require.main && require.main.paths || [];\n\nmodule.exports = function() {\n var paths = Object.keys(require.cache || []);\n\n // module information\n var infos = {};\n\n // paths we have already inspected to avoid traversing again\n var seen = {};\n\n paths.forEach(function(p) {\n var dir = p;\n\n (function updir() {\n var orig = dir;\n dir = path.dirname(orig);\n\n if (!dir || orig === dir || seen[orig]) {\n return;\n }\n else if (main_paths.indexOf(dir) < 0) {\n return updir();\n }\n\n var pkgfile = path.join(orig, 'package.json');\n var exists = fs.existsSync(pkgfile);\n\n seen[orig] = true;\n\n // travel up the tree if no package.json here\n if (!exists) {\n return updir();\n }\n\n try {\n var info = JSON.parse(fs.readFileSync(pkgfile, 'utf8'));\n infos[info.name] = info.version;\n } catch (e) {};\n })();\n });\n\n return infos;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/lsmod/index.js\n// module id = 21\n// module chunks = 0","exports.get = function(belowFn) {\n var oldLimit = Error.stackTraceLimit;\n Error.stackTraceLimit = Infinity;\n\n var dummyObject = {};\n\n var v8Handler = Error.prepareStackTrace;\n Error.prepareStackTrace = function(dummyObject, v8StackTrace) {\n return v8StackTrace;\n };\n Error.captureStackTrace(dummyObject, belowFn || exports.get);\n\n var v8StackTrace = dummyObject.stack;\n Error.prepareStackTrace = v8Handler;\n Error.stackTraceLimit = oldLimit;\n\n return v8StackTrace;\n};\n\nexports.parse = function(err) {\n if (!err.stack) {\n return [];\n }\n\n var self = this;\n var lines = err.stack.split('\\n').slice(1);\n\n return lines\n .map(function(line) {\n if (line.match(/^\\s*[-]{4,}$/)) {\n return self._createParsedCallSite({\n fileName: line,\n lineNumber: null,\n functionName: null,\n typeName: null,\n methodName: null,\n columnNumber: null,\n 'native': null,\n });\n }\n\n var lineMatch = line.match(/at (?:(.+)\\s+)?\\(?(?:(.+?):(\\d+):(\\d+)|([^)]+))\\)?/);\n if (!lineMatch) {\n return;\n }\n\n var object = null;\n var method = null;\n var functionName = null;\n var typeName = null;\n var methodName = null;\n var isNative = (lineMatch[5] === 'native');\n\n if (lineMatch[1]) {\n var methodMatch = lineMatch[1].match(/([^\\.]+)(?:\\.(.+))?/);\n object = methodMatch[1];\n method = methodMatch[2];\n functionName = lineMatch[1];\n typeName = 'Object';\n }\n\n if (method) {\n typeName = object;\n methodName = method;\n }\n\n if (method === '') {\n methodName = null;\n functionName = '';\n }\n\n var properties = {\n fileName: lineMatch[2] || null,\n lineNumber: parseInt(lineMatch[3], 10) || null,\n functionName: functionName,\n typeName: typeName,\n methodName: methodName,\n columnNumber: parseInt(lineMatch[4], 10) || null,\n 'native': isNative,\n };\n\n return self._createParsedCallSite(properties);\n })\n .filter(function(callSite) {\n return !!callSite;\n });\n};\n\nexports._createParsedCallSite = function(properties) {\n var methods = {};\n for (var property in properties) {\n var prefix = 'get';\n if (property === 'native') {\n prefix = 'is';\n }\n var method = prefix + property.substr(0, 1).toUpperCase() + property.substr(1);\n\n (function(property) {\n methods[method] = function() {\n return properties[property];\n }\n })(property);\n }\n\n var callSite = Object.create(methods);\n for (var property in properties) {\n callSite[property] = properties[property];\n }\n\n return callSite;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/stack-trace/lib/stack-trace.js\n// module id = 22\n// module chunks = 0","module.exports = require(\"zlib\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"zlib\"\n// module id = 23\n// module chunks = 0","// Unique ID creation requires a high quality random # generator. We feature\n// detect to determine the best RNG source, normalizing to a function that\n// returns 128-bits of randomness, since that's what's usually required\nvar _rng = require('./lib/rng');\n\n// Maps for number <-> hex string conversion\nvar _byteToHex = [];\nvar _hexToByte = {};\nfor (var i = 0; i < 256; ++i) {\n _byteToHex[i] = (i + 0x100).toString(16).substr(1);\n _hexToByte[_byteToHex[i]] = i;\n}\n\nfunction buff_to_string(buf, offset) {\n var i = offset || 0;\n var bth = _byteToHex;\n return bth[buf[i++]] + bth[buf[i++]] +\n bth[buf[i++]] + bth[buf[i++]] + '-' +\n bth[buf[i++]] + bth[buf[i++]] + '-' +\n bth[buf[i++]] + bth[buf[i++]] + '-' +\n bth[buf[i++]] + bth[buf[i++]] + '-' +\n bth[buf[i++]] + bth[buf[i++]] +\n bth[buf[i++]] + bth[buf[i++]] +\n bth[buf[i++]] + bth[buf[i++]];\n}\n\n// **`v1()` - Generate time-based UUID**\n//\n// Inspired by https://github.com/LiosK/UUID.js\n// and http://docs.python.org/library/uuid.html\n\n// random #'s we need to init node and clockseq\nvar _seedBytes = _rng();\n\n// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)\nvar _nodeId = [\n _seedBytes[0] | 0x01,\n _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]\n];\n\n// Per 4.2.2, randomize (14 bit) clockseq\nvar _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;\n\n// Previous uuid creation time\nvar _lastMSecs = 0, _lastNSecs = 0;\n\n// See https://github.com/broofa/node-uuid for API details\nfunction v1(options, buf, offset) {\n var i = buf && offset || 0;\n var b = buf || [];\n\n options = options || {};\n\n var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq;\n\n // UUID timestamps are 100 nano-second units since the Gregorian epoch,\n // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so\n // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'\n // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.\n var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime();\n\n // Per 4.2.1.2, use count of uuid's generated during the current clock\n // cycle to simulate higher resolution clock\n var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1;\n\n // Time since last uuid creation (in msecs)\n var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;\n\n // Per 4.2.1.2, Bump clockseq on clock regression\n if (dt < 0 && options.clockseq === undefined) {\n clockseq = clockseq + 1 & 0x3fff;\n }\n\n // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new\n // time interval\n if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {\n nsecs = 0;\n }\n\n // Per 4.2.1.2 Throw error if too many uuids are requested\n if (nsecs >= 10000) {\n throw new Error('uuid.v1(): Can\\'t create more than 10M uuids/sec');\n }\n\n _lastMSecs = msecs;\n _lastNSecs = nsecs;\n _clockseq = clockseq;\n\n // Per 4.1.4 - Convert from unix epoch to Gregorian epoch\n msecs += 12219292800000;\n\n // `time_low`\n var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;\n b[i++] = tl >>> 24 & 0xff;\n b[i++] = tl >>> 16 & 0xff;\n b[i++] = tl >>> 8 & 0xff;\n b[i++] = tl & 0xff;\n\n // `time_mid`\n var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;\n b[i++] = tmh >>> 8 & 0xff;\n b[i++] = tmh & 0xff;\n\n // `time_high_and_version`\n b[i++] = tmh >>> 24 & 0xf | 0x10; // include version\n b[i++] = tmh >>> 16 & 0xff;\n\n // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)\n b[i++] = clockseq >>> 8 | 0x80;\n\n // `clock_seq_low`\n b[i++] = clockseq & 0xff;\n\n // `node`\n var node = options.node || _nodeId;\n for (var n = 0; n < 6; ++n) {\n b[i + n] = node[n];\n }\n\n return buf ? buf : buff_to_string(b);\n}\n\n// **`v4()` - Generate random UUID**\n\n// See https://github.com/broofa/node-uuid for API details\nfunction v4(options, buf, offset) {\n // Deprecated - 'format' argument, as supported in v1.2\n var i = buf && offset || 0;\n\n if (typeof(options) == 'string') {\n buf = options == 'binary' ? new Array(16) : null;\n options = null;\n }\n options = options || {};\n\n var rnds = options.random || (options.rng || _rng)();\n\n // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n rnds[6] = (rnds[6] & 0x0f) | 0x40;\n rnds[8] = (rnds[8] & 0x3f) | 0x80;\n\n // Copy bytes to buffer, if provided\n if (buf) {\n for (var ii = 0; ii < 16; ++ii) {\n buf[i + ii] = rnds[ii];\n }\n }\n\n return buf || buff_to_string(rnds);\n}\n\n// Export public API\nvar uuid = v4;\nuuid.v1 = v1;\nuuid.v4 = v4;\n\nmodule.exports = uuid;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/uuid/uuid.js\n// module id = 24\n// module chunks = 0","var rb = require('crypto').randomBytes;\nmodule.exports = function() {\n return rb(16);\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/uuid/lib/rng.js\n// module id = 25\n// module chunks = 0","module.exports = require(\"crypto\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"crypto\"\n// module id = 26\n// module chunks = 0","module.exports = require(\"domain\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"domain\"\n// module id = 27\n// module chunks = 0","module.exports = require(\"module\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"module\"\n// module id = 28\n// module chunks = 0","var map = {\n\t\"./console\": 12,\n\t\"./console.js\": 12,\n\t\"./http\": 13,\n\t\"./http.js\": 13,\n\t\"./instrumentor\": 4,\n\t\"./instrumentor.js\": 4,\n\t\"./pg\": 14,\n\t\"./pg.js\": 14\n};\nfunction webpackContext(req) {\n\treturn __webpack_require__(webpackContextResolve(req));\n};\nfunction webpackContextResolve(req) {\n\tvar id = map[req];\n\tif(!(id + 1)) // check for number or string\n\t\tthrow new Error(\"Cannot find module '\" + req + \"'.\");\n\treturn id;\n};\nwebpackContext.keys = function webpackContextKeys() {\n\treturn Object.keys(map);\n};\nwebpackContext.resolve = webpackContextResolve;\nmodule.exports = webpackContext;\nwebpackContext.id = 29;\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/raven/lib/instrumentation ^\\.\\/.*$\n// module id = 29\n// module chunks = 0","module.exports = require(\"console\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"console\"\n// module id = 30\n// module chunks = 0","module.exports = require(\"os\");\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"os\"\n// module id = 31\n// module chunks = 0","var bar = require('./bar.js');\n\nfunction foo() {\n bar();\n}\n\nmodule.exports = foo;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/foo.js\n// module id = 32\n// module chunks = 0","var path = require('path');\n\nmodule.exports = function bar() {\n throw new Error(path.join('foo', 'bar'));\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/bar.js\n// module id = 33\n// module chunks = 0"],"sourceRoot":""} \ No newline at end of file diff --git a/tests/sentry/lang/javascript/test_plugin.py b/tests/sentry/lang/javascript/test_plugin.py index 3a5f6dd70c8428..cde40c7375ee6b 100644 --- a/tests/sentry/lang/javascript/test_plugin.py +++ b/tests/sentry/lang/javascript/test_plugin.py @@ -1079,11 +1079,44 @@ def test_html_response_for_js(self): } ] - @patch('sentry.lang.javascript.processor.fetch_file') - def test_foo(self, mock_fetch_file): + def test_node_processing(self): + project = self.project + release = Release.objects.create( + organization_id=project.organization_id, + version='nodeabc123', + ) + release.add_project(project) + + f_minified = File.objects.create( + name='dist.bundle.js', + type='release.file', + headers={'Content-Type': 'application/javascript'}, + ) + f_minified.putfile(open(get_fixture_path('dist.bundle.js'), 'rb')) + ReleaseFile.objects.create( + name='~/{}'.format(f_minified.name), + release=release, + organization_id=project.organization_id, + file=f_minified, + ) + + f_sourcemap = File.objects.create( + name='dist.bundle.js.map', + type='release.file', + headers={'Content-Type': 'application/javascript'}, + ) + f_sourcemap.putfile(open(get_fixture_path('dist.bundle.js.map'), 'rb')) + ReleaseFile.objects.create( + name='~/{}'.format(f_sourcemap.name), + release=release, + organization_id=project.organization_id, + file=f_sourcemap, + ) + data = { 'message': 'hello', 'platform': 'node', + 'release': 'nodeabc123', 'sentry.interfaces.Exception': { 'values': [ { @@ -1091,41 +1124,41 @@ def test_foo(self, mock_fetch_file): 'stacktrace': { 'frames': [ { - 'abs_path': 'node_bootstrap.js', - 'filename': 'node_bootstrap.js', - 'lineno': 1, - 'colno': 38, + 'filename': 'app:///dist.bundle.js', + 'function': 'bar', + 'lineno': 9, + 'colno': 2321, }, { - 'abs_path': 'timers.js', - 'filename': 'timers.js', - 'lineno': 1, - 'colno': 39, + 'filename': 'app:///dist.bundle.js', + 'function': 'foo', + 'lineno': 3, + 'colno': 2308, }, { - 'abs_path': 'webpack:///internal', - 'filename': 'internal', - 'lineno': 1, - 'colno': 43, + 'filename': 'app:///dist.bundle.js', + 'function': 'App', + 'lineno': 3, + 'colno': 1011, }, { - 'abs_path': 'webpack:///~/some_dep/file.js', - 'filename': 'file.js', + 'filename': 'app:///dist.bundle.js', + 'function': 'Object.', 'lineno': 1, - 'colno': 41, + 'colno': 1014, }, { - 'abs_path': 'webpack:///./node_modules/file.js', - 'filename': 'file.js', - 'lineno': 1, - 'colno': 42, + 'filename': 'app:///dist.bundle.js', + 'function': '__webpack_require__', + 'lineno': 20, + 'colno': 30, }, { - 'abs_path': 'app:///file.js', - 'filename': 'file.js', - 'lineno': 1, - 'colno': 40, - }, + 'filename': 'app:///dist.bundle.js', + 'function': '', + 'lineno': 18, + 'colno': 63, + } ], }, } @@ -1133,34 +1166,47 @@ def test_foo(self, mock_fetch_file): } } - mock_fetch_file.return_value.body = '\n'.join('hello world') - mock_fetch_file.return_value.encoding = None - resp = self._postWithHeader(data) assert resp.status_code, 200 - assert mock_fetch_file.call_count == 3 - - args, kwargs = mock_fetch_file.call_args_list[0] - assert args[0] == 'app:///file.js' - args, kwargs = mock_fetch_file.call_args_list[1] - assert args[0] == 'webpack:///~/some_dep/file.js' - args, kwargs = mock_fetch_file.call_args_list[2] - assert args[0] == 'webpack:///./node_modules/file.js' - args, kwargs = mock_fetch_file.call_args_list[3] - assert args[0] == 'webpack:///internal' - event = Event.objects.get() exception = event.interfaces['sentry.interfaces.Exception'] frame_list = exception.values[0].stacktrace.frames - assert not frame_list[0].in_app - assert not frame_list[1].in_app - assert not frame_list[2].in_app - assert not frame_list[3].in_app - assert not frame_list[4].in_app - assert frame_list[5].in_app + assert len(frame_list) == 6 + + import pprint + pprint.pprint(frame_list[0].__dict__) + pprint.pprint(frame_list[1].__dict__) + pprint.pprint(frame_list[2].__dict__) + pprint.pprint(frame_list[3].__dict__) + pprint.pprint(frame_list[4].__dict__) + pprint.pprint(frame_list[5].__dict__) + + assert frame_list[0].abs_path == 'webpack:///webpack/bootstrap d9a5a31d9276b73873d3' + assert frame_list[0].function == 'bar' + assert frame_list[0].lineno == 8 + + assert frame_list[1].abs_path == 'webpack:///webpack/bootstrap d9a5a31d9276b73873d3' + assert frame_list[1].function == 'foo' + assert frame_list[1].lineno == 2 + + assert frame_list[2].abs_path == 'webpack:///webpack/bootstrap d9a5a31d9276b73873d3' + assert frame_list[2].function == 'App' + assert frame_list[2].lineno == 2 + + assert frame_list[3].abs_path == 'app:///dist.bundle.js' + assert frame_list[3].function == 'Object.' + assert frame_list[3].lineno == 1 + + assert frame_list[4].abs_path == 'webpack:///webpack/bootstrap d9a5a31d9276b73873d3' + assert frame_list[4].function == '__webpack_require__' + assert frame_list[4].lineno == 19 + + assert frame_list[5].abs_path == 'webpack:///webpack/bootstrap d9a5a31d9276b73873d3' + assert frame_list[5].function == '' + assert frame_list[5].lineno == 16 @responses.activate def test_no_fetch_from_http(self):