diff --git a/packages/browser/src/profiling/integration.ts b/packages/browser/src/profiling/integration.ts index 3f5251c4583f..326af29492cf 100644 --- a/packages/browser/src/profiling/integration.ts +++ b/packages/browser/src/profiling/integration.ts @@ -1,4 +1,4 @@ -import type { EventProcessor, Hub, Integration, Transaction } from '@sentry/types'; +import type { EventEnvelope, EventProcessor, Hub, Integration, Transaction } from '@sentry/types'; import type { Profile } from '@sentry/types/src/profiling'; import { logger } from '@sentry/utils'; @@ -110,7 +110,7 @@ export class BrowserProfilingIntegration implements Integration { } } - addProfilesToEnvelope(envelope, profilesToAddToEnvelope); + addProfilesToEnvelope(envelope as EventEnvelope, profilesToAddToEnvelope); }); } else { logger.warn('[Profiling] Client does not support hooks, profiling will be disabled'); diff --git a/packages/browser/src/profiling/utils.ts b/packages/browser/src/profiling/utils.ts index 1aac3d397ca7..3edb82e0b539 100644 --- a/packages/browser/src/profiling/utils.ts +++ b/packages/browser/src/profiling/utils.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ import { DEFAULT_ENVIRONMENT, getClient, getCurrentHub } from '@sentry/core'; -import type { DebugImage, Envelope, Event, StackFrame, StackParser, Transaction } from '@sentry/types'; +import type { DebugImage, Envelope, Event, EventEnvelope, StackFrame, StackParser, Transaction } from '@sentry/types'; import type { Profile, ThreadCpuProfile } from '@sentry/types/src/profiling'; import { GLOBAL_OBJ, browserPerformanceTimeOrigin, forEachEnvelopeItem, logger, uuid4 } from '@sentry/utils'; @@ -300,13 +300,12 @@ export function convertJSSelfProfileToSampledFormat(input: JSSelfProfile): Profi * Adds items to envelope if they are not already present - mutates the envelope. * @param envelope */ -export function addProfilesToEnvelope(envelope: Envelope, profiles: Profile[]): Envelope { +export function addProfilesToEnvelope(envelope: EventEnvelope, profiles: Profile[]): Envelope { if (!profiles.length) { return envelope; } for (const profile of profiles) { - // @ts-expect-error untyped envelope envelope[1].push([{ type: 'profile' }, profile]); } return envelope; diff --git a/packages/core/src/hub.ts b/packages/core/src/hub.ts index 8306d033cd75..13d5fd059e93 100644 --- a/packages/core/src/hub.ts +++ b/packages/core/src/hub.ts @@ -142,7 +142,7 @@ export class Hub implements HubInterface { */ public pushScope(): Scope { // We want to clone the content of prev scope - const scope = Scope.clone(this.getScope()); + const scope = this.getScope().clone(); this.getStack().push({ client: this.getClient(), scope, @@ -578,7 +578,7 @@ export function ensureHubOnCarrier(carrier: Carrier, parent: Hub = getGlobalHub( // If there's no hub on current domain, or it's an old API, assign a new one if (!hasHubOnCarrier(carrier) || getHubFromCarrier(carrier).isOlderThan(API_VERSION)) { const globalHubTopStack = parent.getStackTop(); - setHubOnCarrier(carrier, new Hub(globalHubTopStack.client, Scope.clone(globalHubTopStack.scope))); + setHubOnCarrier(carrier, new Hub(globalHubTopStack.client, globalHubTopStack.scope.clone())); } } diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index 266087dd6090..ae6fe70e3185 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -110,27 +110,33 @@ export class Scope implements ScopeInterface { /** * Inherit values from the parent scope. - * @param scope to clone. + * @deprecated Use `scope.clone()` and `new Scope()` instead. */ public static clone(scope?: Scope): Scope { + return scope ? scope.clone() : new Scope(); + } + + /** + * Clone this scope instance. + */ + public clone(): Scope { const newScope = new Scope(); - if (scope) { - newScope._breadcrumbs = [...scope._breadcrumbs]; - newScope._tags = { ...scope._tags }; - newScope._extra = { ...scope._extra }; - newScope._contexts = { ...scope._contexts }; - newScope._user = scope._user; - newScope._level = scope._level; - newScope._span = scope._span; - newScope._session = scope._session; - newScope._transactionName = scope._transactionName; - newScope._fingerprint = scope._fingerprint; - newScope._eventProcessors = [...scope._eventProcessors]; - newScope._requestSession = scope._requestSession; - newScope._attachments = [...scope._attachments]; - newScope._sdkProcessingMetadata = { ...scope._sdkProcessingMetadata }; - newScope._propagationContext = { ...scope._propagationContext }; - } + 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 }; + return newScope; } diff --git a/packages/core/src/utils/prepareEvent.ts b/packages/core/src/utils/prepareEvent.ts index 6e25350202b4..46b26070653f 100644 --- a/packages/core/src/utils/prepareEvent.ts +++ b/packages/core/src/utils/prepareEvent.ts @@ -74,10 +74,7 @@ export function prepareEvent( // 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. - let finalScope = scope; - if (hint.captureContext) { - finalScope = Scope.clone(finalScope).update(hint.captureContext); - } + const finalScope = getFinalScope(scope, hint.captureContext); if (hint.mechanism) { addExceptionMechanism(prepared, hint.mechanism); @@ -349,6 +346,16 @@ function normalizeEvent(event: Event | null, depth: number, maxBreadth: number): return normalized; } +function getFinalScope(scope: Scope | undefined, captureContext: CaptureContext | undefined): Scope | undefined { + if (!captureContext) { + return scope; + } + + const finalScope = scope ? scope.clone() : new 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`. diff --git a/packages/opentelemetry-node/test/spanprocessor.test.ts b/packages/opentelemetry-node/test/spanprocessor.test.ts index 5361055755ff..9de394d2232d 100644 --- a/packages/opentelemetry-node/test/spanprocessor.test.ts +++ b/packages/opentelemetry-node/test/spanprocessor.test.ts @@ -5,15 +5,7 @@ import type { Span as OtelSpan } from '@opentelemetry/sdk-trace-base'; import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SemanticAttributes, SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; import type { SpanStatusType } from '@sentry/core'; -import { - Hub, - Scope, - Span as SentrySpan, - Transaction, - addTracingExtensions, - createTransport, - makeMain, -} from '@sentry/core'; +import { Hub, Span as SentrySpan, Transaction, addTracingExtensions, createTransport, makeMain } from '@sentry/core'; import { NodeClient } from '@sentry/node'; import { resolvedSyncPromise } from '@sentry/utils'; @@ -973,7 +965,7 @@ describe('SentrySpanProcessor', () => { hub = new Hub(client); makeMain(hub); - const newHub = new Hub(client, Scope.clone(hub.getScope())); + const newHub = new Hub(client, hub.getScope().clone()); newHub.configureScope(scope => { scope.setTag('foo', 'bar'); }); diff --git a/packages/opentelemetry/src/contextManager.ts b/packages/opentelemetry/src/contextManager.ts index ca9305dfea9b..3b3a6a280928 100644 --- a/packages/opentelemetry/src/contextManager.ts +++ b/packages/opentelemetry/src/contextManager.ts @@ -1,7 +1,8 @@ import type { Context, ContextManager } from '@opentelemetry/api'; import type { Carrier, Hub } from '@sentry/core'; +import { ensureHubOnCarrier } from '@sentry/core'; -import { ensureHubOnCarrier, getCurrentHub, getHubFromCarrier } from './custom/hub'; +import { getCurrentHub, getHubFromCarrier } from './custom/hub'; import { setHubOnContext } from './utils/contextData'; function createNewHub(parent: Hub | undefined): Hub { diff --git a/packages/opentelemetry/src/custom/client.ts b/packages/opentelemetry/src/custom/client.ts index 36648b63c22f..44dd0cf80f4f 100644 --- a/packages/opentelemetry/src/custom/client.ts +++ b/packages/opentelemetry/src/custom/client.ts @@ -3,7 +3,7 @@ import { trace } from '@opentelemetry/api'; import type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; import type { BaseClient, Scope } from '@sentry/core'; import { SDK_VERSION } from '@sentry/core'; -import type { Client, Event, EventHint } from '@sentry/types'; +import type { CaptureContext, Client, Event, EventHint } from '@sentry/types'; import type { OpenTelemetryClient as OpenTelemetryClientInterface } from '../types'; import { OpenTelemetryScope } from './scope'; @@ -65,14 +65,14 @@ export function wrapClientClass< /** * Extends the base `_prepareEvent` so that we can properly handle `captureContext`. - * This uses `Scope.clone()`, which we need to replace with `NodeExperimentalScope.clone()` for this client. + * This uses `Scope.clone()`, which we need to replace with `OpenTelemetryScope.clone()` for this client. */ protected _prepareEvent(event: Event, hint: EventHint, scope?: Scope): PromiseLike { let actualScope = scope; // Remove `captureContext` hint and instead clone already here if (hint && hint.captureContext) { - actualScope = OpenTelemetryScope.clone(scope); + actualScope = getFinalScope(scope, hint.captureContext); delete hint.captureContext; } @@ -83,3 +83,9 @@ export function wrapClientClass< return OpenTelemetryClient as unknown as WrappedClassConstructor; } /* eslint-enable @typescript-eslint/no-explicit-any */ + +function getFinalScope(scope: Scope | undefined, captureContext: CaptureContext): Scope | undefined { + const finalScope = scope ? scope.clone() : new OpenTelemetryScope(); + finalScope.update(captureContext); + return finalScope; +} diff --git a/packages/opentelemetry/src/custom/hub.ts b/packages/opentelemetry/src/custom/hub.ts index 3ecea85b0a6f..ed37ae75b09f 100644 --- a/packages/opentelemetry/src/custom/hub.ts +++ b/packages/opentelemetry/src/custom/hub.ts @@ -13,19 +13,6 @@ export class OpenTelemetryHub extends Hub { public constructor(client?: Client, scope: Scope = new OpenTelemetryScope()) { super(client, scope); } - - /** - * @inheritDoc - */ - public pushScope(): Scope { - // We want to clone the content of prev scope - const scope = OpenTelemetryScope.clone(this.getScope()); - this.getStack().push({ - client: this.getClient(), - scope, - }); - return scope; - } } /** Custom getClient method that uses the custom hub. */ @@ -110,10 +97,7 @@ export function ensureHubOnCarrier(carrier: Carrier, parent: Hub = getGlobalHub( // If there's no hub on current domain, or it's an old API, assign a new one if (!hasHubOnCarrier(carrier) || getHubFromCarrier(carrier).isOlderThan(API_VERSION)) { const globalHubTopStack = parent.getStackTop(); - setHubOnCarrier( - carrier, - new OpenTelemetryHub(globalHubTopStack.client, OpenTelemetryScope.clone(globalHubTopStack.scope)), - ); + setHubOnCarrier(carrier, new OpenTelemetryHub(globalHubTopStack.client, globalHubTopStack.scope.clone())); } } diff --git a/packages/opentelemetry/src/custom/scope.ts b/packages/opentelemetry/src/custom/scope.ts index b65aa437b14f..e206ba8d8096 100644 --- a/packages/opentelemetry/src/custom/scope.ts +++ b/packages/opentelemetry/src/custom/scope.ts @@ -23,24 +23,30 @@ export class OpenTelemetryScope extends Scope { * @inheritDoc */ public static clone(scope?: Scope): Scope { + return scope ? scope.clone() : new OpenTelemetryScope(); + } + + /** + * Clone this scope instance. + */ + public clone(): OpenTelemetryScope { const newScope = new OpenTelemetryScope(); - if (scope) { - newScope._breadcrumbs = [...scope['_breadcrumbs']]; - newScope._tags = { ...scope['_tags'] }; - newScope._extra = { ...scope['_extra'] }; - newScope._contexts = { ...scope['_contexts'] }; - newScope._user = scope['_user']; - newScope._level = scope['_level']; - newScope._span = scope['_span']; - newScope._session = scope['_session']; - newScope._transactionName = scope['_transactionName']; - newScope._fingerprint = scope['_fingerprint']; - newScope._eventProcessors = [...scope['_eventProcessors']]; - newScope._requestSession = scope['_requestSession']; - newScope._attachments = [...scope['_attachments']]; - newScope._sdkProcessingMetadata = { ...scope['_sdkProcessingMetadata'] }; - newScope._propagationContext = { ...scope['_propagationContext'] }; - } + 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'] }; + return newScope; } diff --git a/packages/opentelemetry/src/spanExporter.ts b/packages/opentelemetry/src/spanExporter.ts index d3c3b5b3da90..95ad13997fb9 100644 --- a/packages/opentelemetry/src/spanExporter.ts +++ b/packages/opentelemetry/src/spanExporter.ts @@ -112,8 +112,8 @@ function maybeSend(spans: ReadableSpan[]): ReadableSpan[] { // Now finish the transaction, which will send it together with all the spans // We make sure to use the current span as the activeSpan for this transaction - const scope = getSpanScope(span); - const forkedScope = OpenTelemetryScope.clone(scope as OpenTelemetryScope | undefined) as OpenTelemetryScope; + const scope = getSpanScope(span) as OpenTelemetryScope | undefined; + const forkedScope = scope ? scope.clone() : new OpenTelemetryScope(); forkedScope.activeSpan = span as unknown as Span; transaction.finishWithScope(convertOtelTimeToSeconds(span.endTime), forkedScope); diff --git a/packages/opentelemetry/test/custom/scope.test.ts b/packages/opentelemetry/test/custom/scope.test.ts index 6c96fab2f3c5..41827fcd772d 100644 --- a/packages/opentelemetry/test/custom/scope.test.ts +++ b/packages/opentelemetry/test/custom/scope.test.ts @@ -30,6 +30,7 @@ describe('NodeExperimentalScope', () => { scope['_attachments'] = [{ data: '123', filename: 'test.txt' }]; scope['_sdkProcessingMetadata'] = { sdk: 'bar' }; + // eslint-disable-next-line deprecation/deprecation const scope2 = OpenTelemetryScope.clone(scope); expect(scope2).toBeInstanceOf(OpenTelemetryScope); @@ -68,6 +69,7 @@ describe('NodeExperimentalScope', () => { }); it('clone() works without existing scope', () => { + // eslint-disable-next-line deprecation/deprecation const scope = OpenTelemetryScope.clone(undefined); expect(scope).toBeInstanceOf(OpenTelemetryScope); diff --git a/packages/replay/package.json b/packages/replay/package.json index 035390206954..17ace93fc627 100644 --- a/packages/replay/package.json +++ b/packages/replay/package.json @@ -48,8 +48,8 @@ "devDependencies": { "@babel/core": "^7.17.5", "@sentry-internal/replay-worker": "7.86.0", - "@sentry-internal/rrweb": "2.2.0", - "@sentry-internal/rrweb-snapshot": "2.2.0", + "@sentry-internal/rrweb": "2.5.0", + "@sentry-internal/rrweb-snapshot": "2.5.0", "fflate": "^0.8.1", "jsdom-worker": "^0.2.1" }, diff --git a/packages/replay/src/coreHandlers/handleBeforeSendEvent.ts b/packages/replay/src/coreHandlers/handleBeforeSendEvent.ts new file mode 100644 index 000000000000..d7276897497e --- /dev/null +++ b/packages/replay/src/coreHandlers/handleBeforeSendEvent.ts @@ -0,0 +1,42 @@ +import type { ErrorEvent, Event } from '@sentry/types'; + +import type { ReplayContainer } from '../types'; +import { createBreadcrumb } from '../util/createBreadcrumb'; +import { isErrorEvent } from '../util/eventUtils'; +import { addBreadcrumbEvent } from './util/addBreadcrumbEvent'; + +type BeforeSendEventCallback = (event: Event) => void; + +/** + * Returns a listener to be added to `client.on('afterSendErrorEvent, listener)`. + */ +export function handleBeforeSendEvent(replay: ReplayContainer): BeforeSendEventCallback { + return (event: Event) => { + if (!replay.isEnabled() || !isErrorEvent(event)) { + return; + } + + handleHydrationError(replay, event); + }; +} + +function handleHydrationError(replay: ReplayContainer, event: ErrorEvent): void { + const exceptionValue = event.exception && event.exception.values && event.exception.values[0].value; + if (typeof exceptionValue !== 'string') { + return; + } + + if ( + // Only matches errors in production builds of react-dom + // Example https://reactjs.org/docs/error-decoder.html?invariant=423 + exceptionValue.match(/reactjs\.org\/docs\/error-decoder\.html\?invariant=(418|419|422|423|425)/) || + // Development builds of react-dom + // Example Text: content did not match. Server: "A" Client: "B" + exceptionValue.match(/(hydration|content does not match|did not match)/i) + ) { + const breadcrumb = createBreadcrumb({ + category: 'replay.hydrate-error', + }); + addBreadcrumbEvent(replay, breadcrumb); + } +} diff --git a/packages/replay/src/replay.ts b/packages/replay/src/replay.ts index 635db47245d6..8e1740845c7b 100644 --- a/packages/replay/src/replay.ts +++ b/packages/replay/src/replay.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ // TODO: We might want to split this file up import { EventType, record } from '@sentry-internal/rrweb'; import { captureException, getClient, getCurrentHub } from '@sentry/core'; -import type { ReplayRecordingMode, Transaction } from '@sentry/types'; +import type { Event as SentryEvent, ReplayRecordingMode, Transaction } from '@sentry/types'; import { logger } from '@sentry/utils'; import { diff --git a/packages/replay/src/util/addGlobalListeners.ts b/packages/replay/src/util/addGlobalListeners.ts index 828731bf814f..fac2b278e666 100644 --- a/packages/replay/src/util/addGlobalListeners.ts +++ b/packages/replay/src/util/addGlobalListeners.ts @@ -4,6 +4,7 @@ import type { Client, DynamicSamplingContext } from '@sentry/types'; import { addClickKeypressInstrumentationHandler, addHistoryInstrumentationHandler } from '@sentry/utils'; import { handleAfterSendEvent } from '../coreHandlers/handleAfterSendEvent'; +import { handleBeforeSendEvent } from '../coreHandlers/handleBeforeSendEvent'; import { handleDomListener } from '../coreHandlers/handleDom'; import { handleGlobalEventListener } from '../coreHandlers/handleGlobalEvent'; import { handleHistorySpanListener } from '../coreHandlers/handleHistory'; @@ -35,6 +36,7 @@ export function addGlobalListeners(replay: ReplayContainer): void { // If a custom client has no hooks yet, we continue to use the "old" implementation if (hasHooks(client)) { + client.on('beforeSendEvent', handleBeforeSendEvent(replay)); client.on('afterSendEvent', handleAfterSendEvent(replay)); client.on('createDsc', (dsc: DynamicSamplingContext) => { const replayId = replay.getSessionId(); diff --git a/packages/replay/test/integration/coreHandlers/handleBeforeSendEvent.test.ts b/packages/replay/test/integration/coreHandlers/handleBeforeSendEvent.test.ts new file mode 100644 index 000000000000..92cdc5a88698 --- /dev/null +++ b/packages/replay/test/integration/coreHandlers/handleBeforeSendEvent.test.ts @@ -0,0 +1,80 @@ +import { handleBeforeSendEvent } from '../../../src/coreHandlers/handleBeforeSendEvent'; +import type { ReplayContainer } from '../../../src/replay'; +import { Error } from '../../fixtures/error'; +import { resetSdkMock } from '../../mocks/resetSdkMock'; +import { useFakeTimers } from '../../utils/use-fake-timers'; + +useFakeTimers(); +let replay: ReplayContainer; + +describe('Integration | coreHandlers | handleBeforeSendEvent', () => { + afterEach(() => { + replay.stop(); + }); + + it('adds a hydration breadcrumb on development hydration error', async () => { + ({ replay } = await resetSdkMock({ + replayOptions: { + stickySession: false, + }, + sentryOptions: { + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + }, + })); + + const handler = handleBeforeSendEvent(replay); + const addBreadcrumbSpy = jest.spyOn(replay, 'throttledAddEvent'); + + const error = Error(); + error.exception.values[0].value = 'Text content did not match. Server: "A" Client: "B"'; + handler(error); + + expect(addBreadcrumbSpy).toHaveBeenCalledTimes(1); + expect(addBreadcrumbSpy).toHaveBeenCalledWith({ + data: { + payload: { + category: 'replay.hydrate-error', + timestamp: expect.any(Number), + type: 'default', + }, + tag: 'breadcrumb', + }, + timestamp: expect.any(Number), + type: 5, + }); + }); + + it('adds a hydration breadcrumb on production hydration error', async () => { + ({ replay } = await resetSdkMock({ + replayOptions: { + stickySession: false, + }, + sentryOptions: { + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + }, + })); + + const handler = handleBeforeSendEvent(replay); + const addBreadcrumbSpy = jest.spyOn(replay, 'throttledAddEvent'); + + const error = Error(); + error.exception.values[0].value = 'https://reactjs.org/docs/error-decoder.html?invariant=423'; + handler(error); + + expect(addBreadcrumbSpy).toHaveBeenCalledTimes(1); + expect(addBreadcrumbSpy).toHaveBeenCalledWith({ + data: { + payload: { + category: 'replay.hydrate-error', + timestamp: expect.any(Number), + type: 'default', + }, + tag: 'breadcrumb', + }, + timestamp: expect.any(Number), + type: 5, + }); + }); +}); diff --git a/packages/types/src/envelope.ts b/packages/types/src/envelope.ts index fbe593dd21f9..8d47206f4217 100644 --- a/packages/types/src/envelope.ts +++ b/packages/types/src/envelope.ts @@ -3,6 +3,7 @@ import type { ClientReport } from './clientreport'; import type { DsnComponents } from './dsn'; import type { Event } from './event'; import type { FeedbackEvent } from './feedback'; +import type { Profile } from './profiling'; import type { ReplayEvent, ReplayRecordingData } from './replay'; import type { SdkInfo } from './sdkinfo'; import type { SerializedSession, Session, SessionAggregates } from './session'; @@ -77,6 +78,7 @@ type ReplayEventItemHeaders = { type: 'replay_event' }; type ReplayRecordingItemHeaders = { type: 'replay_recording'; length: number }; type CheckInItemHeaders = { type: 'check_in' }; type StatsdItemHeaders = { type: 'statsd' }; +type ProfileItemHeaders = { type: 'profile' }; export type EventItem = BaseEnvelopeItem; export type AttachmentItem = BaseEnvelopeItem; @@ -91,6 +93,7 @@ type ReplayEventItem = BaseEnvelopeItem; type ReplayRecordingItem = BaseEnvelopeItem; export type StatsdItem = BaseEnvelopeItem; export type FeedbackItem = BaseEnvelopeItem; +export type ProfileItem = BaseEnvelopeItem; export type EventEnvelopeHeaders = { event_id: string; sent_at: string; trace?: DynamicSamplingContext }; type SessionEnvelopeHeaders = { sent_at: string }; @@ -101,7 +104,7 @@ type StatsdEnvelopeHeaders = BaseEnvelopeHeaders; export type EventEnvelope = BaseEnvelope< EventEnvelopeHeaders, - EventItem | AttachmentItem | UserFeedbackItem | FeedbackItem + EventItem | AttachmentItem | UserFeedbackItem | FeedbackItem | ProfileItem >; export type SessionEnvelope = BaseEnvelope; export type ClientReportEnvelope = BaseEnvelope; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 615bf71ff785..5603854170f5 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -44,6 +44,7 @@ export type { CheckInEnvelope, StatsdItem, StatsdEnvelope, + ProfileItem, } from './envelope'; export type { ExtendedError } from './error'; export type { Event, EventHint, EventType, ErrorEvent, TransactionEvent } from './event'; diff --git a/yarn.lock b/yarn.lock index ef0bcb3c6b02..8d0f29d0c6c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5048,33 +5048,33 @@ semver "7.3.2" semver-intersect "1.4.0" -"@sentry-internal/rrdom@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.2.0.tgz#edc292e55263e1e2541a9ed282ff998aae272a0a" - integrity sha512-pX2YxpZfKxfbeEKG+sc0WzlSMUsG9mgwK3IioeVulNovUmus6UhLLQHYeZEsQg0WehClCgBRZakk8qEFXbmwdg== +"@sentry-internal/rrdom@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.5.0.tgz#5571b9791f1efc79141237bf8d4faaf7b872b7bb" + integrity sha512-1wGVQXoC7wnk6/T8BdHd6fK9F4fGn6huQGtss77uCvX9gJaXpfwUDV7Rbj8bOhTMn8bXsIIhpuBg7OMMgXYTzA== dependencies: - "@sentry-internal/rrweb-snapshot" "2.2.0" + "@sentry-internal/rrweb-snapshot" "2.5.0" -"@sentry-internal/rrweb-snapshot@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.2.0.tgz#23a231ffd8ef734c2665690e8040a4ffc2faaf9f" - integrity sha512-txHynfjaJHLJvS+Kf9DOJyL7ur/nIBSJsF7OVKoJYJr43uRJzOyJ/SdliIrGz71IpVOCkuBa/ufRRVEVneRDDg== +"@sentry-internal/rrweb-snapshot@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.5.0.tgz#c13faa437cf703568a5dae4ba67e4e0adc356a23" + integrity sha512-7UMeAPLzXCDmB6CnuGUSHQ4TZ1YJc7a4u8HHoQhbL4gYhi2kgsmE5KMkulV5IKOUqZLlwFkzcXTqf37BXcEMUQ== -"@sentry-internal/rrweb-types@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.2.0.tgz#79a76a8d5c436cf18cdee604ab95a9e3049d1949" - integrity sha512-0x7gL9cEF/rrdx1feIORD8+pHihJVA2t1wMRnT+PyiEJZToNJO08Mpbe2rlWgovfK3kGDj5GVFVXsPKGTj8c3A== +"@sentry-internal/rrweb-types@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.5.0.tgz#5ecd52f83b561ae011f2db1f9e3d0ea3273f1b32" + integrity sha512-m9X9F3rOqEDUPaNxlhs6NNDB6GPJf4jbdNqFrKa9Qj567S5iInQTj5yplEUuD84ydpXFgoOTm2Kp2sLhP8SEwg== dependencies: - "@sentry-internal/rrweb-snapshot" "2.2.0" + "@sentry-internal/rrweb-snapshot" "2.5.0" -"@sentry-internal/rrweb@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.2.0.tgz#f9b538f16718b88f6ca99b3393c5fbebc274bc06" - integrity sha512-gcdC9uuw6b5WZp1wsqC8p8yBN8bmiZN7X7UaxMrhBwH9TifbYxK1gNbgeKOI/GzZF9OVbduXYm3UDt9ZS1njGg== +"@sentry-internal/rrweb@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.5.0.tgz#32c8496bd113bc93bef6d8f11100e8b5193e4225" + integrity sha512-kl6Tyk77j+zREgZVg0YH8YOn7xQxYB+1RRzqRL8rMrKou64uRSGVLMjhb4MR3i4hizkp4ILV3TinaondSx6v8w== dependencies: - "@sentry-internal/rrdom" "2.2.0" - "@sentry-internal/rrweb-snapshot" "2.2.0" - "@sentry-internal/rrweb-types" "2.2.0" + "@sentry-internal/rrdom" "2.5.0" + "@sentry-internal/rrweb-snapshot" "2.5.0" + "@sentry-internal/rrweb-types" "2.5.0" "@types/css-font-loading-module" "0.0.7" "@xstate/fsm" "^1.4.0" base64-arraybuffer "^1.0.1"