Object.defineProperty(exports, '__esModule', { value: true }); const utils = require('@sentry/utils'); const constants = require('../constants.js'); const eventProcessors = require('../eventProcessors.js'); const scope = require('../scope.js'); const applyScopeDataToEvent = require('./applyScopeDataToEvent.js'); const spanUtils = require('./spanUtils.js'); /** * This type makes sure that we get either a CaptureContext, OR an EventHint. * It does not allow mixing them, which could lead to unexpected outcomes, e.g. this is disallowed: * { user: { id: '123' }, mechanism: { handled: false } } */ /** * Adds common information to events. * * The information includes release and environment from `options`, * breadcrumbs and context (extra, tags and user) from the scope. * * Information that is already present in the event is never overwritten. For * nested objects, such as the context, keys are merged. * * Note: This also triggers callbacks for `addGlobalEventProcessor`, but not `beforeSend`. * * @param event The original event. * @param hint May contain additional information about the original exception. * @param scope A scope containing event metadata. * @returns A new event with more information. * @hidden */ function prepareEvent( options, event, hint, scope$1, client, isolationScope, ) { const { normalizeDepth = 3, normalizeMaxBreadth = 1000 } = options; const prepared = { ...event, event_id: event.event_id || hint.event_id || utils.uuid4(), timestamp: event.timestamp || utils.dateTimestampInSeconds(), }; const integrations = hint.integrations || options.integrations.map(i => i.name); applyClientOptions(prepared, options); applyIntegrationsMetadata(prepared, integrations); // Only put debug IDs onto frames for error events. if (event.type === undefined) { applyDebugIds(prepared, options.stackParser); } // If we have scope given to us, use it as the base for further modifications. // This allows us to prevent unnecessary copying of data if `captureContext` is not provided. const finalScope = getFinalScope(scope$1, hint.captureContext); if (hint.mechanism) { utils.addExceptionMechanism(prepared, hint.mechanism); } const clientEventProcessors = client && client.getEventProcessors ? client.getEventProcessors() : []; // This should be the last thing called, since we want that // {@link Hub.addEventProcessor} gets the finished prepared event. // Merge scope data together const data = scope.getGlobalScope().getScopeData(); if (isolationScope) { const isolationData = isolationScope.getScopeData(); applyScopeDataToEvent.mergeScopeData(data, isolationData); } if (finalScope) { const finalScopeData = finalScope.getScopeData(); applyScopeDataToEvent.mergeScopeData(data, finalScopeData); } const attachments = [...(hint.attachments || []), ...data.attachments]; if (attachments.length) { hint.attachments = attachments; } applyScopeDataToEvent.applyScopeDataToEvent(prepared, data); // TODO (v8): Update this order to be: Global > Client > Scope const eventProcessors$1 = [ ...clientEventProcessors, // eslint-disable-next-line deprecation/deprecation ...eventProcessors.getGlobalEventProcessors(), // Run scope event processors _after_ all other processors ...data.eventProcessors, ]; const result = eventProcessors.notifyEventProcessors(eventProcessors$1, prepared, hint); return result.then(evt => { if (evt) { // We apply the debug_meta field only after all event processors have ran, so that if any event processors modified // file names (e.g.the RewriteFrames integration) the filename -> debug ID relationship isn't destroyed. // This should not cause any PII issues, since we're only moving data that is already on the event and not adding // any new data applyDebugMeta(evt); } if (typeof normalizeDepth === 'number' && normalizeDepth > 0) { return normalizeEvent(evt, normalizeDepth, normalizeMaxBreadth); } return evt; }); } /** * Enhances event using the client configuration. * It takes care of all "static" values like environment, release and `dist`, * as well as truncating overly long values. * @param event event instance to be enhanced */ function applyClientOptions(event, options) { const { environment, release, dist, maxValueLength = 250 } = options; if (!('environment' in event)) { event.environment = 'environment' in options ? environment : constants.DEFAULT_ENVIRONMENT; } if (event.release === undefined && release !== undefined) { event.release = release; } if (event.dist === undefined && dist !== undefined) { event.dist = dist; } if (event.message) { event.message = utils.truncate(event.message, maxValueLength); } const exception = event.exception && event.exception.values && event.exception.values[0]; if (exception && exception.value) { exception.value = utils.truncate(exception.value, maxValueLength); } const request = event.request; if (request && request.url) { request.url = utils.truncate(request.url, maxValueLength); } } const debugIdStackParserCache = new WeakMap(); /** * Puts debug IDs into the stack frames of an error event. */ function applyDebugIds(event, stackParser) { const debugIdMap = utils.GLOBAL_OBJ._sentryDebugIds; if (!debugIdMap) { return; } let debugIdStackFramesCache; const cachedDebugIdStackFrameCache = debugIdStackParserCache.get(stackParser); if (cachedDebugIdStackFrameCache) { debugIdStackFramesCache = cachedDebugIdStackFrameCache; } else { debugIdStackFramesCache = new Map(); debugIdStackParserCache.set(stackParser, debugIdStackFramesCache); } // Build a map of filename -> debug_id const filenameDebugIdMap = Object.keys(debugIdMap).reduce((acc, debugIdStackTrace) => { let parsedStack; const cachedParsedStack = debugIdStackFramesCache.get(debugIdStackTrace); if (cachedParsedStack) { parsedStack = cachedParsedStack; } else { parsedStack = stackParser(debugIdStackTrace); debugIdStackFramesCache.set(debugIdStackTrace, parsedStack); } for (let i = parsedStack.length - 1; i >= 0; i--) { const stackFrame = parsedStack[i]; if (stackFrame.filename) { acc[stackFrame.filename] = debugIdMap[debugIdStackTrace]; break; } } return acc; }, {}); try { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion event.exception.values.forEach(exception => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion exception.stacktrace.frames.forEach(frame => { if (frame.filename) { frame.debug_id = filenameDebugIdMap[frame.filename]; } }); }); } catch (e) { // To save bundle size we're just try catching here instead of checking for the existence of all the different objects. } } /** * Moves debug IDs from the stack frames of an error event into the debug_meta field. */ function applyDebugMeta(event) { // Extract debug IDs and filenames from the stack frames on the event. const filenameDebugIdMap = {}; try { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion event.exception.values.forEach(exception => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion exception.stacktrace.frames.forEach(frame => { if (frame.debug_id) { if (frame.abs_path) { filenameDebugIdMap[frame.abs_path] = frame.debug_id; } else if (frame.filename) { filenameDebugIdMap[frame.filename] = frame.debug_id; } delete frame.debug_id; } }); }); } catch (e) { // To save bundle size we're just try catching here instead of checking for the existence of all the different objects. } if (Object.keys(filenameDebugIdMap).length === 0) { return; } // Fill debug_meta information event.debug_meta = event.debug_meta || {}; event.debug_meta.images = event.debug_meta.images || []; const images = event.debug_meta.images; Object.keys(filenameDebugIdMap).forEach(filename => { images.push({ type: 'sourcemap', code_file: filename, debug_id: filenameDebugIdMap[filename], }); }); } /** * This function adds all used integrations to the SDK info in the event. * @param event The event that will be filled with all integrations. */ function applyIntegrationsMetadata(event, integrationNames) { if (integrationNames.length > 0) { event.sdk = event.sdk || {}; event.sdk.integrations = [...(event.sdk.integrations || []), ...integrationNames]; } } /** * Applies `normalize` function on necessary `Event` attributes to make them safe for serialization. * Normalized keys: * - `breadcrumbs.data` * - `user` * - `contexts` * - `extra` * @param event Event * @returns Normalized event */ function normalizeEvent(event, depth, maxBreadth) { if (!event) { return null; } const normalized = { ...event, ...(event.breadcrumbs && { breadcrumbs: event.breadcrumbs.map(b => ({ ...b, ...(b.data && { data: utils.normalize(b.data, depth, maxBreadth), }), })), }), ...(event.user && { user: utils.normalize(event.user, depth, maxBreadth), }), ...(event.contexts && { contexts: utils.normalize(event.contexts, depth, maxBreadth), }), ...(event.extra && { extra: utils.normalize(event.extra, depth, maxBreadth), }), }; // event.contexts.trace stores information about a Transaction. Similarly, // event.spans[] stores information about child Spans. Given that a // Transaction is conceptually a Span, normalization should apply to both // Transactions and Spans consistently. // For now the decision is to skip normalization of Transactions and Spans, // so this block overwrites the normalized event to add back the original // Transaction information prior to normalization. if (event.contexts && event.contexts.trace && normalized.contexts) { normalized.contexts.trace = event.contexts.trace; // event.contexts.trace.data may contain circular/dangerous data so we need to normalize it if (event.contexts.trace.data) { normalized.contexts.trace.data = utils.normalize(event.contexts.trace.data, depth, maxBreadth); } } // event.spans[].data may contain circular/dangerous data so we need to normalize it if (event.spans) { normalized.spans = event.spans.map(span => { const data = spanUtils.spanToJSON(span).data; if (data) { // This is a bit weird, as we generally have `Span` instances here, but to be safe we do not assume so // eslint-disable-next-line deprecation/deprecation span.data = utils.normalize(data, depth, maxBreadth); } return span; }); } return normalized; } function getFinalScope(scope$1, captureContext) { if (!captureContext) { return scope$1; } const finalScope = scope$1 ? scope$1.clone() : new scope.Scope(); finalScope.update(captureContext); return finalScope; } /** * Parse either an `EventHint` directly, or convert a `CaptureContext` to an `EventHint`. * This is used to allow to update method signatures that used to accept a `CaptureContext` but should now accept an `EventHint`. */ function parseEventHintOrCaptureContext( hint, ) { if (!hint) { return undefined; } // If you pass a Scope or `() => Scope` as CaptureContext, we just return this as captureContext if (hintIsScopeOrFunction(hint)) { return { captureContext: hint }; } if (hintIsScopeContext(hint)) { return { captureContext: hint, }; } return hint; } function hintIsScopeOrFunction( hint, ) { return hint instanceof scope.Scope || typeof hint === 'function'; } const captureContextKeys = [ 'user', 'level', 'extra', 'contexts', 'tags', 'fingerprint', 'requestSession', 'propagationContext', ] ; function hintIsScopeContext(hint) { return Object.keys(hint).some(key => captureContextKeys.includes(key )); } exports.applyDebugIds = applyDebugIds; exports.applyDebugMeta = applyDebugMeta; exports.parseEventHintOrCaptureContext = parseEventHintOrCaptureContext; exports.prepareEvent = prepareEvent; //# sourceMappingURL=prepareEvent.js.map