Object.defineProperty(exports, '__esModule', { value: true }); const utils = require('@sentry/utils'); const eventProcessors = require('./eventProcessors.js'); const session = require('./session.js'); const applyScopeDataToEvent = require('./utils/applyScopeDataToEvent.js'); /** * Default value for maximum number of breadcrumbs added to an event. */ const DEFAULT_MAX_BREADCRUMBS = 100; /** * The global scope is kept in this module. * When accessing this via `getGlobalScope()` we'll make sure to set one if none is currently present. */ let globalScope; /** * Holds additional event information. {@link Scope.applyToEvent} will be * called by the client before an event will be sent. */ class Scope { /** Flag if notifying is happening. */ /** Callback for client to receive scope changes. */ /** Callback list that will be called after {@link applyToEvent}. */ /** Array of breadcrumbs. */ /** User */ /** Tags */ /** Extra */ /** Contexts */ /** Attachments */ /** Propagation Context for distributed tracing */ /** * A place to stash data which is needed at some point in the SDK's event processing pipeline but which shouldn't get * sent to Sentry */ /** Fingerprint */ /** Severity */ // eslint-disable-next-line deprecation/deprecation /** * Transaction Name */ /** Span */ /** Session */ /** Request Mode Session Status */ /** The client on this scope */ // NOTE: Any field which gets added here should get added not only to the constructor but also to the `clone` method. constructor() { this._notifyingListeners = false; this._scopeListeners = []; this._eventProcessors = []; this._breadcrumbs = []; this._attachments = []; this._user = {}; this._tags = {}; this._extra = {}; this._contexts = {}; this._sdkProcessingMetadata = {}; this._propagationContext = generatePropagationContext(); } /** * Inherit values from the parent scope. * @deprecated Use `scope.clone()` and `new Scope()` instead. */ static clone(scope) { return scope ? scope.clone() : new Scope(); } /** * Clone this scope instance. */ clone() { const newScope = new Scope(); newScope._breadcrumbs = [...this._breadcrumbs]; newScope._tags = { ...this._tags }; newScope._extra = { ...this._extra }; newScope._contexts = { ...this._contexts }; newScope._user = this._user; newScope._level = this._level; newScope._span = this._span; newScope._session = this._session; newScope._transactionName = this._transactionName; newScope._fingerprint = this._fingerprint; newScope._eventProcessors = [...this._eventProcessors]; newScope._requestSession = this._requestSession; newScope._attachments = [...this._attachments]; newScope._sdkProcessingMetadata = { ...this._sdkProcessingMetadata }; newScope._propagationContext = { ...this._propagationContext }; newScope._client = this._client; return newScope; } /** Update the client on the scope. */ setClient(client) { this._client = client; } /** * Get the client assigned to this scope. * * It is generally recommended to use the global function `Sentry.getClient()` instead, unless you know what you are doing. */ getClient() { return this._client; } /** * Add internal on change listener. Used for sub SDKs that need to store the scope. * @hidden */ addScopeListener(callback) { this._scopeListeners.push(callback); } /** * @inheritDoc */ addEventProcessor(callback) { this._eventProcessors.push(callback); return this; } /** * @inheritDoc */ setUser(user) { // If null is passed we want to unset everything, but still define keys, // so that later down in the pipeline any existing values are cleared. this._user = user || { email: undefined, id: undefined, ip_address: undefined, segment: undefined, username: undefined, }; if (this._session) { session.updateSession(this._session, { user }); } this._notifyScopeListeners(); return this; } /** * @inheritDoc */ getUser() { return this._user; } /** * @inheritDoc */ getRequestSession() { return this._requestSession; } /** * @inheritDoc */ setRequestSession(requestSession) { this._requestSession = requestSession; return this; } /** * @inheritDoc */ setTags(tags) { this._tags = { ...this._tags, ...tags, }; this._notifyScopeListeners(); return this; } /** * @inheritDoc */ setTag(key, value) { this._tags = { ...this._tags, [key]: value }; this._notifyScopeListeners(); return this; } /** * @inheritDoc */ setExtras(extras) { this._extra = { ...this._extra, ...extras, }; this._notifyScopeListeners(); return this; } /** * @inheritDoc */ setExtra(key, extra) { this._extra = { ...this._extra, [key]: extra }; this._notifyScopeListeners(); return this; } /** * @inheritDoc */ setFingerprint(fingerprint) { this._fingerprint = fingerprint; this._notifyScopeListeners(); return this; } /** * @inheritDoc */ setLevel( // eslint-disable-next-line deprecation/deprecation level, ) { this._level = level; this._notifyScopeListeners(); return this; } /** * Sets the transaction name on the scope for future events. * @deprecated Use extra or tags instead. */ setTransactionName(name) { this._transactionName = name; this._notifyScopeListeners(); return this; } /** * @inheritDoc */ setContext(key, context) { if (context === null) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete this._contexts[key]; } else { this._contexts[key] = context; } this._notifyScopeListeners(); return this; } /** * Sets the Span on the scope. * @param span Span * @deprecated Instead of setting a span on a scope, use `startSpan()`/`startSpanManual()` instead. */ setSpan(span) { this._span = span; this._notifyScopeListeners(); return this; } /** * Returns the `Span` if there is one. * @deprecated Use `getActiveSpan()` instead. */ getSpan() { return this._span; } /** * Returns the `Transaction` attached to the scope (if there is one). * @deprecated You should not rely on the transaction, but just use `startSpan()` APIs instead. */ getTransaction() { // Often, this span (if it exists at all) will be a transaction, but it's not guaranteed to be. Regardless, it will // have a pointer to the currently-active transaction. const span = this._span; // Cannot replace with getRootSpan because getRootSpan returns a span, not a transaction // Also, this method will be removed anyway. // eslint-disable-next-line deprecation/deprecation return span && span.transaction; } /** * @inheritDoc */ setSession(session) { if (!session) { delete this._session; } else { this._session = session; } this._notifyScopeListeners(); return this; } /** * @inheritDoc */ getSession() { return this._session; } /** * @inheritDoc */ update(captureContext) { if (!captureContext) { return this; } const scopeToMerge = typeof captureContext === 'function' ? captureContext(this) : captureContext; if (scopeToMerge instanceof Scope) { const scopeData = scopeToMerge.getScopeData(); this._tags = { ...this._tags, ...scopeData.tags }; this._extra = { ...this._extra, ...scopeData.extra }; this._contexts = { ...this._contexts, ...scopeData.contexts }; if (scopeData.user && Object.keys(scopeData.user).length) { this._user = scopeData.user; } if (scopeData.level) { this._level = scopeData.level; } if (scopeData.fingerprint.length) { this._fingerprint = scopeData.fingerprint; } if (scopeToMerge.getRequestSession()) { this._requestSession = scopeToMerge.getRequestSession(); } if (scopeData.propagationContext) { this._propagationContext = scopeData.propagationContext; } } else if (utils.isPlainObject(scopeToMerge)) { const scopeContext = captureContext ; this._tags = { ...this._tags, ...scopeContext.tags }; this._extra = { ...this._extra, ...scopeContext.extra }; this._contexts = { ...this._contexts, ...scopeContext.contexts }; if (scopeContext.user) { this._user = scopeContext.user; } if (scopeContext.level) { this._level = scopeContext.level; } if (scopeContext.fingerprint) { this._fingerprint = scopeContext.fingerprint; } if (scopeContext.requestSession) { this._requestSession = scopeContext.requestSession; } if (scopeContext.propagationContext) { this._propagationContext = scopeContext.propagationContext; } } return this; } /** * @inheritDoc */ clear() { this._breadcrumbs = []; this._tags = {}; this._extra = {}; this._user = {}; this._contexts = {}; this._level = undefined; this._transactionName = undefined; this._fingerprint = undefined; this._requestSession = undefined; this._span = undefined; this._session = undefined; this._notifyScopeListeners(); this._attachments = []; this._propagationContext = generatePropagationContext(); return this; } /** * @inheritDoc */ addBreadcrumb(breadcrumb, maxBreadcrumbs) { const maxCrumbs = typeof maxBreadcrumbs === 'number' ? maxBreadcrumbs : DEFAULT_MAX_BREADCRUMBS; // No data has been changed, so don't notify scope listeners if (maxCrumbs <= 0) { return this; } const mergedBreadcrumb = { timestamp: utils.dateTimestampInSeconds(), ...breadcrumb, }; const breadcrumbs = this._breadcrumbs; breadcrumbs.push(mergedBreadcrumb); this._breadcrumbs = breadcrumbs.length > maxCrumbs ? breadcrumbs.slice(-maxCrumbs) : breadcrumbs; this._notifyScopeListeners(); return this; } /** * @inheritDoc */ getLastBreadcrumb() { return this._breadcrumbs[this._breadcrumbs.length - 1]; } /** * @inheritDoc */ clearBreadcrumbs() { this._breadcrumbs = []; this._notifyScopeListeners(); return this; } /** * @inheritDoc */ addAttachment(attachment) { this._attachments.push(attachment); return this; } /** * @inheritDoc * @deprecated Use `getScopeData()` instead. */ getAttachments() { const data = this.getScopeData(); return data.attachments; } /** * @inheritDoc */ clearAttachments() { this._attachments = []; return this; } /** @inheritDoc */ getScopeData() { const { _breadcrumbs, _attachments, _contexts, _tags, _extra, _user, _level, _fingerprint, _eventProcessors, _propagationContext, _sdkProcessingMetadata, _transactionName, _span, } = this; return { breadcrumbs: _breadcrumbs, attachments: _attachments, contexts: _contexts, tags: _tags, extra: _extra, user: _user, level: _level, fingerprint: _fingerprint || [], eventProcessors: _eventProcessors, propagationContext: _propagationContext, sdkProcessingMetadata: _sdkProcessingMetadata, transactionName: _transactionName, span: _span, }; } /** * Applies data from the scope to the event and runs all event processors on it. * * @param event Event * @param hint Object containing additional information about the original exception, for use by the event processors. * @hidden * @deprecated Use `applyScopeDataToEvent()` directly */ applyToEvent( event, hint = {}, additionalEventProcessors = [], ) { applyScopeDataToEvent.applyScopeDataToEvent(event, this.getScopeData()); // TODO (v8): Update this order to be: Global > Client > Scope const eventProcessors$1 = [ ...additionalEventProcessors, // eslint-disable-next-line deprecation/deprecation ...eventProcessors.getGlobalEventProcessors(), ...this._eventProcessors, ]; return eventProcessors.notifyEventProcessors(eventProcessors$1, event, hint); } /** * Add data which will be accessible during event processing but won't get sent to Sentry */ setSDKProcessingMetadata(newData) { this._sdkProcessingMetadata = { ...this._sdkProcessingMetadata, ...newData }; return this; } /** * @inheritDoc */ setPropagationContext(context) { this._propagationContext = context; return this; } /** * @inheritDoc */ getPropagationContext() { return this._propagationContext; } /** * Capture an exception for this scope. * * @param exception The exception to capture. * @param hint Optinal additional data to attach to the Sentry event. * @returns the id of the captured Sentry event. */ captureException(exception, hint) { const eventId = hint && hint.event_id ? hint.event_id : utils.uuid4(); if (!this._client) { utils.logger.warn('No client configured on scope - will not capture exception!'); return eventId; } const syntheticException = new Error('Sentry syntheticException'); this._client.captureException( exception, { originalException: exception, syntheticException, ...hint, event_id: eventId, }, this, ); return eventId; } /** * Capture a message for this scope. * * @param message The message to capture. * @param level An optional severity level to report the message with. * @param hint Optional additional data to attach to the Sentry event. * @returns the id of the captured message. */ captureMessage(message, level, hint) { const eventId = hint && hint.event_id ? hint.event_id : utils.uuid4(); if (!this._client) { utils.logger.warn('No client configured on scope - will not capture message!'); return eventId; } const syntheticException = new Error(message); this._client.captureMessage( message, level, { originalException: message, syntheticException, ...hint, event_id: eventId, }, this, ); return eventId; } /** * Captures a manually created event for this scope and sends it to Sentry. * * @param exception The event to capture. * @param hint Optional additional data to attach to the Sentry event. * @returns the id of the captured event. */ captureEvent(event, hint) { const eventId = hint && hint.event_id ? hint.event_id : utils.uuid4(); if (!this._client) { utils.logger.warn('No client configured on scope - will not capture event!'); return eventId; } this._client.captureEvent(event, { ...hint, event_id: eventId }, this); return eventId; } /** * This will be called on every set call. */ _notifyScopeListeners() { // We need this check for this._notifyingListeners to be able to work on scope during updates // If this check is not here we'll produce endless recursion when something is done with the scope // during the callback. if (!this._notifyingListeners) { this._notifyingListeners = true; this._scopeListeners.forEach(callback => { callback(this); }); this._notifyingListeners = false; } } } /** * Get the global scope. * This scope is applied to _all_ events. */ function getGlobalScope() { if (!globalScope) { globalScope = new Scope(); } return globalScope; } /** * This is mainly needed for tests. * DO NOT USE this, as this is an internal API and subject to change. * @hidden */ function setGlobalScope(scope) { globalScope = scope; } function generatePropagationContext() { return { traceId: utils.uuid4(), spanId: utils.uuid4().substring(16), }; } exports.Scope = Scope; exports.getGlobalScope = getGlobalScope; exports.setGlobalScope = setGlobalScope; //# sourceMappingURL=scope.js.map