import { node } from './node-stack-trace.js'; export { filenameIsInApp } from './node-stack-trace.js'; const STACKTRACE_FRAME_LIMIT = 50; // Used to sanitize webpack (error: *) wrapped stack errors const WEBPACK_ERROR_REGEXP = /\(error: (.*)\)/; const STRIP_FRAME_REGEXP = /captureMessage|captureException/; /** * Creates a stack parser with the supplied line parsers * * StackFrames are returned in the correct order for Sentry Exception * frames and with Sentry SDK internal frames removed from the top and bottom * */ function createStackParser(...parsers) { const sortedParsers = parsers.sort((a, b) => a[0] - b[0]).map(p => p[1]); return (stack, skipFirst = 0) => { const frames = []; const lines = stack.split('\n'); for (let i = skipFirst; i < lines.length; i++) { const line = lines[i]; // Ignore lines over 1kb as they are unlikely to be stack frames. // Many of the regular expressions use backtracking which results in run time that increases exponentially with // input size. Huge strings can result in hangs/Denial of Service: // https://github.com/getsentry/sentry-javascript/issues/2286 if (line.length > 1024) { continue; } // https://github.com/getsentry/sentry-javascript/issues/5459 // Remove webpack (error: *) wrappers const cleanedLine = WEBPACK_ERROR_REGEXP.test(line) ? line.replace(WEBPACK_ERROR_REGEXP, '$1') : line; // https://github.com/getsentry/sentry-javascript/issues/7813 // Skip Error: lines if (cleanedLine.match(/\S*Error: /)) { continue; } for (const parser of sortedParsers) { const frame = parser(cleanedLine); if (frame) { frames.push(frame); break; } } if (frames.length >= STACKTRACE_FRAME_LIMIT) { break; } } return stripSentryFramesAndReverse(frames); }; } /** * Gets a stack parser implementation from Options.stackParser * @see Options * * If options contains an array of line parsers, it is converted into a parser */ function stackParserFromStackParserOptions(stackParser) { if (Array.isArray(stackParser)) { return createStackParser(...stackParser); } return stackParser; } /** * Removes Sentry frames from the top and bottom of the stack if present and enforces a limit of max number of frames. * Assumes stack input is ordered from top to bottom and returns the reverse representation so call site of the * function that caused the crash is the last frame in the array. * @hidden */ function stripSentryFramesAndReverse(stack) { if (!stack.length) { return []; } const localStack = Array.from(stack); // If stack starts with one of our API calls, remove it (starts, meaning it's the top of the stack - aka last call) if (/sentryWrapped/.test(localStack[localStack.length - 1].function || '')) { localStack.pop(); } // Reversing in the middle of the procedure allows us to just pop the values off the stack localStack.reverse(); // If stack ends with one of our internal API calls, remove it (ends, meaning it's the bottom of the stack - aka top-most call) if (STRIP_FRAME_REGEXP.test(localStack[localStack.length - 1].function || '')) { localStack.pop(); // When using synthetic events, we will have a 2 levels deep stack, as `new Error('Sentry syntheticException')` // is produced within the hub itself, making it: // // Sentry.captureException() // getCurrentHub().captureException() // // instead of just the top `Sentry` call itself. // This forces us to possibly strip an additional frame in the exact same was as above. if (STRIP_FRAME_REGEXP.test(localStack[localStack.length - 1].function || '')) { localStack.pop(); } } return localStack.slice(0, STACKTRACE_FRAME_LIMIT).map(frame => ({ ...frame, filename: frame.filename || localStack[localStack.length - 1].filename, function: frame.function || '?', })); } const defaultFunctionName = ''; /** * Safely extract function name from itself */ function getFunctionName(fn) { try { if (!fn || typeof fn !== 'function') { return defaultFunctionName; } return fn.name || defaultFunctionName; } catch (e) { // Just accessing custom props in some Selenium environments // can cause a "Permission denied" exception (see raven-js#495). return defaultFunctionName; } } /** * Node.js stack line parser * * This is in @sentry/utils so it can be used from the Electron SDK in the browser for when `nodeIntegration == true`. * This allows it to be used without referencing or importing any node specific code which causes bundlers to complain */ function nodeStackLineParser(getModule) { return [90, node(getModule)]; } export { createStackParser, getFunctionName, nodeStackLineParser, stackParserFromStackParserOptions, stripSentryFramesAndReverse }; //# sourceMappingURL=stacktrace.js.map