From a70376e2cfaa47a88d42c78b34bbf35ed13e513b Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Mon, 13 Mar 2023 11:28:53 -0400 Subject: [PATCH 01/54] feat(replay): Reduce time limit before pausing a recording (#7356) Reduce the time limit before pausing a recording from `MAX_SESSION_LIFE` (1 hour) to `SESSION_IDLE_DURATION` (5 minutes). This reduces the amount of empty replays that are caused by a DOM mutation on a timer. An example of this is on sentry.io where there is a time component that updates every x seconds. As a reminder, there are three events that will break the idle timeout: * mouse click * user input in a form field * a navigation Closes https://github.com/getsentry/sentry-javascript/issues/7352 --- .../suites/replay/sessionInactive/init.js | 2 +- packages/replay/src/replay.ts | 14 +- .../test/integration/errorSampleRate.test.ts | 87 ++++- .../replay/test/integration/session.test.ts | 310 +++++------------- 4 files changed, 186 insertions(+), 227 deletions(-) diff --git a/packages/integration-tests/suites/replay/sessionInactive/init.js b/packages/integration-tests/suites/replay/sessionInactive/init.js index f1b0345e71d7..a3da64ec3bae 100644 --- a/packages/integration-tests/suites/replay/sessionInactive/init.js +++ b/packages/integration-tests/suites/replay/sessionInactive/init.js @@ -17,6 +17,6 @@ Sentry.init({ }); window.Replay._replay.timeouts = { - sessionIdle: 300000, // default: 5min + sessionIdle: 1000, // default: 5min maxSessionLife: 2000, // this is usually 60min, but we want to test this with shorter times }; diff --git a/packages/replay/src/replay.ts b/packages/replay/src/replay.ts index 939dd0b8375c..7f397855d17f 100644 --- a/packages/replay/src/replay.ts +++ b/packages/replay/src/replay.ts @@ -392,11 +392,19 @@ export class ReplayContainer implements ReplayContainerInterface { const oldSessionId = this.getSessionId(); // Prevent starting a new session if the last user activity is older than - // MAX_SESSION_LIFE. Otherwise non-user activity can trigger a new + // SESSION_IDLE_DURATION. Otherwise non-user activity can trigger a new // session+recording. This creates noisy replays that do not have much // content in them. - if (this._lastActivity && isExpired(this._lastActivity, this.timeouts.maxSessionLife)) { - // Pause recording + if ( + this._lastActivity && + isExpired(this._lastActivity, this.timeouts.sessionIdle) && + this.session && + this.session.sampled === 'session' + ) { + // Pause recording only for session-based replays. Otherwise, resuming + // will create a new replay and will conflict with users who only choose + // to record error-based replays only. (e.g. the resumed replay will not + // contain a reference to an error) this.pause(); return; } diff --git a/packages/replay/test/integration/errorSampleRate.test.ts b/packages/replay/test/integration/errorSampleRate.test.ts index 3eb2288a31fb..1c4ef227db6c 100644 --- a/packages/replay/test/integration/errorSampleRate.test.ts +++ b/packages/replay/test/integration/errorSampleRate.test.ts @@ -269,9 +269,15 @@ describe('Integration | errorSampleRate', () => { expect(replay).not.toHaveLastSentReplay(); }); - it('stops replay if user has been idle for more than 15 minutes and comes back to move their mouse', async () => { + // When the error session records as a normal session, we want to stop + // recording after the session ends. Otherwise, we get into a state where the + // new session is a session type replay (this could conflict with the session + // sample rate of 0.0), or an error session that has no errors. Instead we + // simply stop the session replay completely and wait for a new page load to + // resample. + it('stops replay if session exceeds MAX_SESSION_LIFE and does not start a new session thereafter', async () => { // Idle for 15 minutes - jest.advanceTimersByTime(15 * 60000); + jest.advanceTimersByTime(MAX_SESSION_LIFE + 1); const TEST_EVENT = { data: { name: 'lost event' }, @@ -289,6 +295,42 @@ describe('Integration | errorSampleRate', () => { expect(replay).not.toHaveLastSentReplay(); expect(replay.isEnabled()).toBe(false); expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); + + domHandler({ + name: 'click', + }); + + // Remains disabled! + expect(replay.isEnabled()).toBe(false); + }); + + // Should behave the same as above test + it('stops replay if user has been idle for more than SESSION_IDLE_DURATION and does not start a new session thereafter', async () => { + // Idle for 15 minutes + jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1); + + const TEST_EVENT = { + data: { name: 'lost event' }, + timestamp: BASE_TIMESTAMP, + type: 3, + }; + mockRecord._emitter(TEST_EVENT); + expect(replay).not.toHaveLastSentReplay(); + + jest.runAllTimers(); + await new Promise(process.nextTick); + + // We stop recording after SESSION_IDLE_DURATION of inactivity in error mode + expect(replay).not.toHaveLastSentReplay(); + expect(replay.isEnabled()).toBe(false); + expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); + + domHandler({ + name: 'click', + }); + + // Remains disabled! + expect(replay.isEnabled()).toBe(false); }); it('has the correct timestamps with deferred root event and last replay update', async () => { @@ -375,7 +417,46 @@ describe('Integration | errorSampleRate', () => { }); }); - it('stops replay when session expires', async () => { + it('stops replay when user goes idle', async () => { + jest.setSystemTime(BASE_TIMESTAMP); + + const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 3 }; + mockRecord._emitter(TEST_EVENT); + + expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); + expect(replay).not.toHaveLastSentReplay(); + + jest.runAllTimers(); + await new Promise(process.nextTick); + + captureException(new Error('testing')); + + jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); + await new Promise(process.nextTick); + + expect(replay).toHaveLastSentReplay(); + + // Now wait after session expires - should stop recording + mockRecord.takeFullSnapshot.mockClear(); + (getCurrentHub().getClient()!.getTransport()!.send as unknown as jest.SpyInstance).mockClear(); + + // Go idle + jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1); + await new Promise(process.nextTick); + + mockRecord._emitter(TEST_EVENT); + + expect(replay).not.toHaveLastSentReplay(); + + jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); + await new Promise(process.nextTick); + + expect(replay).not.toHaveLastSentReplay(); + expect(mockRecord.takeFullSnapshot).toHaveBeenCalledTimes(0); + expect(replay.isEnabled()).toBe(false); + }); + + it('stops replay when session exceeds max length', async () => { jest.setSystemTime(BASE_TIMESTAMP); const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 3 }; diff --git a/packages/replay/test/integration/session.test.ts b/packages/replay/test/integration/session.test.ts index d6820db61da7..2492b5ee913b 100644 --- a/packages/replay/test/integration/session.test.ts +++ b/packages/replay/test/integration/session.test.ts @@ -9,6 +9,7 @@ import { WINDOW, } from '../../src/constants'; import type { ReplayContainer } from '../../src/replay'; +import type { Session } from '../../src/types'; import { addEvent } from '../../src/util/addEvent'; import { createPerformanceSpans } from '../../src/util/createPerformanceSpans'; import { BASE_TIMESTAMP } from '../index'; @@ -55,7 +56,9 @@ describe('Integration | session', () => { }); }); - it('creates a new session and triggers a full dom snapshot when document becomes visible after [SESSION_IDLE_DURATION]ms', () => { + // Require a "user interaction" to start a new session, visibility is not enough. This can be noisy + // (e.g. rapidly switching tabs/window focus) and leads to empty sessions. + it('does not create a new session when document becomes visible after [SESSION_IDLE_DURATION]ms', () => { Object.defineProperty(document, 'visibilityState', { configurable: true, get: function () { @@ -63,169 +66,29 @@ describe('Integration | session', () => { }, }); - const initialSession = replay.session; + const initialSession = { ...replay.session } as Session; jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1); document.dispatchEvent(new Event('visibilitychange')); - expect(mockRecord.takeFullSnapshot).toHaveBeenLastCalledWith(true); - - // Should have created a new session - expect(replay).not.toHaveSameSession(initialSession); - }); - - it('does not create a new session if user hides the tab and comes back within [SESSION_IDLE_DURATION] seconds', () => { - const initialSession = replay.session; - - Object.defineProperty(document, 'visibilityState', { - configurable: true, - get: function () { - return 'hidden'; - }, - }); - document.dispatchEvent(new Event('visibilitychange')); - expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); - expect(replay).toHaveSameSession(initialSession); - - // User comes back before `SESSION_IDLE_DURATION` elapses - jest.advanceTimersByTime(SESSION_IDLE_DURATION - 1); - Object.defineProperty(document, 'visibilityState', { - configurable: true, - get: function () { - return 'visible'; - }, - }); - document.dispatchEvent(new Event('visibilitychange')); - expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); - // Should NOT have created a new session expect(replay).toHaveSameSession(initialSession); }); - it('creates a new session if user has been idle for more than 15 minutes and comes back to move their mouse', async () => { - const initialSession = replay.session; - - expect(initialSession?.id).toBeDefined(); - - // Idle for 15 minutes - const FIFTEEN_MINUTES = 15 * 60000; - jest.advanceTimersByTime(FIFTEEN_MINUTES); - - // TBD: We are currently deciding that this event will get dropped, but - // this could/should change in the future. - const TEST_EVENT = { - data: { name: 'lost event' }, - timestamp: BASE_TIMESTAMP, - type: 3, - }; - mockRecord._emitter(TEST_EVENT); - expect(replay).not.toHaveLastSentReplay(); - - await new Promise(process.nextTick); - - // Instead of recording the above event, a full snapshot will occur. - // - // TODO: We could potentially figure out a way to save the last session, - // and produce a checkout based on a previous checkout + updates, and then - // replay the event on top. Or maybe replay the event on top of a refresh - // snapshot. - expect(mockRecord.takeFullSnapshot).toHaveBeenCalledWith(true); - - // Should be a new session - expect(replay).not.toHaveSameSession(initialSession); - - // Replay does not send immediately because checkout was due to expired session - expect(replay).not.toHaveLastSentReplay(); - - // Now do a click - domHandler({ - name: 'click', - }); - - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); - - const newTimestamp = BASE_TIMESTAMP + FIFTEEN_MINUTES; - const breadcrumbTimestamp = newTimestamp + 20; // I don't know where this 20ms comes from - - expect(replay).toHaveLastSentReplay({ - recordingData: JSON.stringify([ - { data: { isCheckout: true }, timestamp: newTimestamp, type: 2 }, - { - type: 5, - timestamp: breadcrumbTimestamp, - data: { - tag: 'breadcrumb', - payload: { - timestamp: breadcrumbTimestamp / 1000, - type: 'default', - category: 'ui.click', - message: '', - data: {}, - }, - }, - }, - ]), - }); - }); - - it('should have a session after setup', () => { - expect(replay.session).toMatchObject({ - lastActivity: BASE_TIMESTAMP, - started: BASE_TIMESTAMP, - }); - expect(replay.session?.id).toBeDefined(); - expect(replay.session?.segmentId).toBeDefined(); - }); - - it('clears session', () => { - clearSession(replay); - expect(WINDOW.sessionStorage.getItem(REPLAY_SESSION_KEY)).toBe(null); - expect(replay.session).toBe(undefined); - }); - - it('creates a new session and triggers a full dom snapshot when document becomes visible after [SESSION_IDLE_DURATION]ms', () => { - Object.defineProperty(document, 'visibilityState', { - configurable: true, - get: function () { - return 'visible'; - }, - }); - - const initialSession = replay.session; - - jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1); - - document.dispatchEvent(new Event('visibilitychange')); - - expect(mockRecord.takeFullSnapshot).toHaveBeenLastCalledWith(true); - - // Should have created a new session - expect(replay).not.toHaveSameSession(initialSession); - }); - - it('creates a new session and triggers a full dom snapshot when document becomes focused after [SESSION_IDLE_DURATION]ms', () => { - Object.defineProperty(document, 'visibilityState', { - configurable: true, - get: function () { - return 'visible'; - }, - }); - - const initialSession = replay.session; + it('does not create a new session when document becomes focused after [SESSION_IDLE_DURATION]ms', () => { + const initialSession = { ...replay.session } as Session; jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1); WINDOW.dispatchEvent(new Event('focus')); - expect(mockRecord.takeFullSnapshot).toHaveBeenLastCalledWith(true); - - // Should have created a new session - expect(replay).not.toHaveSameSession(initialSession); + expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); + expect(replay).toHaveSameSession(initialSession); }); it('does not create a new session if user hides the tab and comes back within [SESSION_IDLE_DURATION] seconds', () => { - const initialSession = replay.session; + const initialSession = { ...replay.session } as Session; Object.defineProperty(document, 'visibilityState', { configurable: true, @@ -252,8 +115,8 @@ describe('Integration | session', () => { expect(replay).toHaveSameSession(initialSession); }); - it('creates a new session if user has been idle for 15 minutes and comes back to click their mouse', async () => { - const initialSession = replay.session; + it('creates a new session if user has been idle for more than SESSION_IDLE_DURATION and comes back to click their mouse', async () => { + const initialSession = { ...replay.session } as Session; expect(initialSession?.id).toBeDefined(); expect(replay.getContext()).toEqual( @@ -268,44 +131,59 @@ describe('Integration | session', () => { value: new URL(url), }); - // Idle for 15 minutes - const FIFTEEN_MINUTES = 15 * 60000; - jest.advanceTimersByTime(FIFTEEN_MINUTES); + const ELAPSED = SESSION_IDLE_DURATION + 1; + jest.advanceTimersByTime(ELAPSED); - // TBD: We are currently deciding that this event will get dropped, but - // this could/should change in the future. + // Session has become in an idle state + // + // This event will put the Replay SDK into a paused state const TEST_EVENT = { data: { name: 'lost event' }, timestamp: BASE_TIMESTAMP, type: 3, }; mockRecord._emitter(TEST_EVENT); - expect(replay).not.toHaveLastSentReplay(); - await new Promise(process.nextTick); + // performance events can still be collected while recording is stopped + // TODO: we may want to prevent `addEvent` from adding to buffer when user is inactive + replay.addUpdate(() => { + createPerformanceSpans(replay, [ + { + type: 'navigation.navigate', + name: 'foo', + start: BASE_TIMESTAMP + ELAPSED, + end: BASE_TIMESTAMP + ELAPSED + 100, + }, + ]); + return true; + }); - // Instead of recording the above event, a full snapshot will occur. - // - // TODO: We could potentially figure out a way to save the last session, - // and produce a checkout based on a previous checkout + updates, and then - // replay the event on top. Or maybe replay the event on top of a refresh - // snapshot. - expect(mockRecord.takeFullSnapshot).toHaveBeenCalledWith(true); + await new Promise(process.nextTick); expect(replay).not.toHaveLastSentReplay(); + expect(replay.isPaused()).toBe(true); + expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); + expect(replay).toHaveSameSession(initialSession); + expect(mockRecord).toHaveBeenCalledTimes(1); - // Should be a new session - expect(replay).not.toHaveSameSession(initialSession); - - // Now do a click + // Now do a click which will create a new session and start recording again domHandler({ name: 'click', }); + // This is not called because we have to start recording + expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); + expect(mockRecord).toHaveBeenCalledTimes(2); + + // Should be a new session + expect(replay).not.toHaveSameSession(initialSession); + + // Replay does not send immediately because checkout was due to expired session + expect(replay).not.toHaveLastSentReplay(); + await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); - const newTimestamp = BASE_TIMESTAMP + FIFTEEN_MINUTES; - const breadcrumbTimestamp = newTimestamp + 20; // I don't know where this 20ms comes from + const newTimestamp = BASE_TIMESTAMP + ELAPSED + 20; expect(replay).toHaveLastSentReplay({ recordingPayloadHeader: { segment_id: 0 }, @@ -313,11 +191,11 @@ describe('Integration | session', () => { { data: { isCheckout: true }, timestamp: newTimestamp, type: 2 }, { type: 5, - timestamp: breadcrumbTimestamp, + timestamp: newTimestamp, data: { tag: 'breadcrumb', payload: { - timestamp: breadcrumbTimestamp / 1000, + timestamp: newTimestamp / 1000, type: 'default', category: 'ui.click', message: '', @@ -329,18 +207,35 @@ describe('Integration | session', () => { }); // `_context` should be reset when a new session is created - expect(replay.getContext()).toEqual( - expect.objectContaining({ - initialUrl: 'http://dummy/', - initialTimestamp: newTimestamp, - }), - ); + expect(replay.getContext()).toEqual({ + earliestEvent: null, + initialUrl: 'http://dummy/', + initialTimestamp: newTimestamp, + urls: [], + errorIds: new Set(), + traceIds: new Set(), + }); }); - it('does not record if user has been idle for more than MAX_SESSION_LIFE and only starts a new session after a user action', async () => { + it('should have a session after setup', () => { + expect(replay.session).toMatchObject({ + lastActivity: BASE_TIMESTAMP, + started: BASE_TIMESTAMP, + }); + expect(replay.session?.id).toBeDefined(); + expect(replay.session?.segmentId).toBeDefined(); + }); + + it('clears session', () => { + clearSession(replay); + expect(WINDOW.sessionStorage.getItem(REPLAY_SESSION_KEY)).toBe(null); + expect(replay.session).toBe(undefined); + }); + + it('creates a new session if current session exceeds MAX_SESSION_LIFE', async () => { jest.clearAllMocks(); - const initialSession = replay.session; + const initialSession = { ...replay.session } as Session; expect(initialSession?.id).toBeDefined(); expect(replay.getContext()).toEqual( @@ -355,80 +250,55 @@ describe('Integration | session', () => { value: new URL(url), }); - // Idle for MAX_SESSION_LIFE - jest.advanceTimersByTime(MAX_SESSION_LIFE); + // Advanced past MAX_SESSION_LIFE + const ELAPSED = MAX_SESSION_LIFE + 1; + jest.advanceTimersByTime(ELAPSED); + // Update activity so as to not consider session to be idling + replay['_updateUserActivity'](); + replay['_updateSessionActivity'](); - // These events will not get flushed and will eventually be dropped because user is idle and session is expired + // This should trigger a new session const TEST_EVENT = { data: { name: 'lost event' }, - timestamp: MAX_SESSION_LIFE, + timestamp: ELAPSED, type: 3, }; mockRecord._emitter(TEST_EVENT); - // performance events can still be collected while recording is stopped - // TODO: we may want to prevent `addEvent` from adding to buffer when user is inactive - replay.addUpdate(() => { - createPerformanceSpans(replay, [ - { - type: 'navigation.navigate', - name: 'foo', - start: BASE_TIMESTAMP + MAX_SESSION_LIFE, - end: BASE_TIMESTAMP + MAX_SESSION_LIFE + 100, - }, - ]); - return true; - }); - - WINDOW.dispatchEvent(new Event('blur')); - await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); - expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); + expect(replay).not.toHaveSameSession(initialSession); + expect(mockRecord.takeFullSnapshot).toHaveBeenCalled(); expect(replay).not.toHaveLastSentReplay(); - // Should be the same session because user has been idle and no events have caused a new session to be created - expect(replay).toHaveSameSession(initialSession); - // @ts-ignore private - expect(replay._stopRecording).toBeUndefined(); + expect(replay._stopRecording).toBeDefined(); // Now do a click domHandler({ name: 'click', }); - // This should still be thrown away - mockRecord._emitter(TEST_EVENT); + + const newTimestamp = BASE_TIMESTAMP + ELAPSED; const NEW_TEST_EVENT = { data: { name: 'test' }, - timestamp: BASE_TIMESTAMP + MAX_SESSION_LIFE + DEFAULT_FLUSH_MIN_DELAY + 20, + timestamp: newTimestamp + DEFAULT_FLUSH_MIN_DELAY + 20, type: 3, }; - mockRecord._emitter(NEW_TEST_EVENT); - // new session is created jest.runAllTimers(); - await new Promise(process.nextTick); - - expect(replay).not.toHaveSameSession(initialSession); await advanceTimers(DEFAULT_FLUSH_MIN_DELAY); - const newTimestamp = BASE_TIMESTAMP + MAX_SESSION_LIFE + DEFAULT_FLUSH_MIN_DELAY + 20; // I don't know where this 20ms comes from - const breadcrumbTimestamp = newTimestamp; - - jest.runAllTimers(); - await new Promise(process.nextTick); - expect(replay).toHaveLastSentReplay({ recordingPayloadHeader: { segment_id: 0 }, recordingData: JSON.stringify([ { data: { isCheckout: true }, timestamp: newTimestamp, type: 2 }, { type: 5, - timestamp: breadcrumbTimestamp, + timestamp: newTimestamp, data: { tag: 'breadcrumb', payload: { - timestamp: breadcrumbTimestamp / 1000, + timestamp: newTimestamp / 1000, type: 'default', category: 'ui.click', message: '', From ba99e7cdf725725e5a1b99e9d814353dbb3ae2b6 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Mon, 13 Mar 2023 16:35:42 +0100 Subject: [PATCH 02/54] test(sveltekit): Switch to vitest (#7438) --- package.json | 4 +- packages/sveltekit/jest.config.js | 3 - packages/sveltekit/package.json | 4 +- .../sveltekit/test/client/handleError.test.ts | 19 +- packages/sveltekit/test/client/sdk.test.ts | 6 +- .../sveltekit/test/config/vitePlugins.test.ts | 15 +- .../sveltekit/test/server/handleError.test.ts | 19 +- packages/sveltekit/test/server/sdk.test.ts | 4 +- packages/sveltekit/tsconfig.json | 2 +- packages/sveltekit/tsconfig.test.json | 2 +- packages/sveltekit/vite.config.ts | 3 + tsconfig.dev.json | 4 +- vite/vite.config.ts | 17 + yarn.lock | 348 +++++++++++++++++- 14 files changed, 407 insertions(+), 43 deletions(-) delete mode 100644 packages/sveltekit/jest.config.js create mode 100644 packages/sveltekit/vite.config.ts create mode 100644 vite/vite.config.ts diff --git a/package.json b/package.json index d106efb1743d..dda4cf8e57e2 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "@types/node": "~10.17.0", "@types/rimraf": "^3.0.2", "@types/sinon": "^7.0.11", + "@vitest/coverage-c8": "^0.29.2", "acorn": "^8.7.0", "chai": "^4.1.2", "codecov": "^3.6.5", @@ -112,7 +113,8 @@ "ts-node": "10.9.1", "tslib": "^2.3.1", "typedoc": "^0.18.0", - "typescript": "3.8.3" + "typescript": "3.8.3", + "vitest": "^0.29.2" }, "resolutions": { "**/agent-base": "5" diff --git a/packages/sveltekit/jest.config.js b/packages/sveltekit/jest.config.js deleted file mode 100644 index 222e6a817c7b..000000000000 --- a/packages/sveltekit/jest.config.js +++ /dev/null @@ -1,3 +0,0 @@ -const baseConfig = require('../../jest/jest.config.js'); - -module.exports = baseConfig; diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index 18bfa482535f..51e7c01b35d8 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -63,8 +63,8 @@ "lint:eslint": "eslint . --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "yarn test:unit", - "test:unit": "jest", - "test:watch": "jest --watch" + "test:unit": "vitest", + "test:watch": "vitest --watch" }, "volta": { "extends": "../../package.json" diff --git a/packages/sveltekit/test/client/handleError.test.ts b/packages/sveltekit/test/client/handleError.test.ts index 49bcb6a0c57f..6260d3dc4827 100644 --- a/packages/sveltekit/test/client/handleError.test.ts +++ b/packages/sveltekit/test/client/handleError.test.ts @@ -4,14 +4,15 @@ import { Scope } from '@sentry/svelte'; // adding a custom resolver, which will take too much time. // eslint-disable-next-line import/no-unresolved import type { HandleClientError, NavigationEvent } from '@sveltejs/kit'; +import { vi } from 'vitest'; import { handleErrorWithSentry } from '../../src/client/handleError'; -const mockCaptureException = jest.fn(); +const mockCaptureException = vi.fn(); let mockScope = new Scope(); -jest.mock('@sentry/svelte', () => { - const original = jest.requireActual('@sentry/core'); +vi.mock('@sentry/svelte', async () => { + const original = (await vi.importActual('@sentry/core')) as any; return { ...original, captureException: (err: unknown, cb: (arg0: unknown) => unknown) => { @@ -22,10 +23,10 @@ jest.mock('@sentry/svelte', () => { }; }); -const mockAddExceptionMechanism = jest.fn(); +const mockAddExceptionMechanism = vi.fn(); -jest.mock('@sentry/utils', () => { - const original = jest.requireActual('@sentry/utils'); +vi.mock('@sentry/utils', async () => { + const original = (await vi.importActual('@sentry/utils')) as any; return { ...original, addExceptionMechanism: (...args: unknown[]) => mockAddExceptionMechanism(...args), @@ -68,15 +69,15 @@ describe('handleError', () => { it('calls captureException', async () => { const wrappedHandleError = handleErrorWithSentry(handleError); const mockError = new Error('test'); - const returnVal = await wrappedHandleError({ error: mockError, event: navigationEvent }); + const returnVal = (await wrappedHandleError({ error: mockError, event: navigationEvent })) as any; - expect(returnVal!.message).toEqual('Whoops!'); + expect(returnVal.message).toEqual('Whoops!'); expect(mockCaptureException).toHaveBeenCalledTimes(1); expect(mockCaptureException).toHaveBeenCalledWith(mockError, expect.any(Function)); }); it('adds an exception mechanism', async () => { - const addEventProcessorSpy = jest.spyOn(mockScope, 'addEventProcessor').mockImplementationOnce(callback => { + const addEventProcessorSpy = vi.spyOn(mockScope, 'addEventProcessor').mockImplementationOnce(callback => { void callback({}, { event_id: 'fake-event-id' }); return mockScope; }); diff --git a/packages/sveltekit/test/client/sdk.test.ts b/packages/sveltekit/test/client/sdk.test.ts index 5ed924a60ccc..ce594c6b75f1 100644 --- a/packages/sveltekit/test/client/sdk.test.ts +++ b/packages/sveltekit/test/client/sdk.test.ts @@ -1,14 +1,16 @@ import { getCurrentHub } from '@sentry/core'; import * as SentrySvelte from '@sentry/svelte'; import { SDK_VERSION, WINDOW } from '@sentry/svelte'; +import { vi } from 'vitest'; import { init } from '../../src/client/sdk'; -const svelteInit = jest.spyOn(SentrySvelte, 'init'); + +const svelteInit = vi.spyOn(SentrySvelte, 'init'); describe('Sentry client SDK', () => { describe('init', () => { afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); WINDOW.__SENTRY__.hub = undefined; }); diff --git a/packages/sveltekit/test/config/vitePlugins.test.ts b/packages/sveltekit/test/config/vitePlugins.test.ts index 9e7c3ec2d788..f04077383d49 100644 --- a/packages/sveltekit/test/config/vitePlugins.test.ts +++ b/packages/sveltekit/test/config/vitePlugins.test.ts @@ -1,7 +1,16 @@ -import * as fs from 'fs'; +import type * as fs from 'fs'; +import { vi } from 'vitest'; import { injectSentryInitPlugin } from '../../src/config/vitePlugins'; +vi.mock('fs', async () => { + const original = await vi.importActual('fs'); + return { + ...original, + existsSync: vi.fn().mockReturnValue(true), + }; +}); + describe('injectSentryInitPlugin', () => { it('has its basic properties set', () => { expect(injectSentryInitPlugin.name).toBe('sentry-init-injection-plugin'); @@ -9,9 +18,7 @@ describe('injectSentryInitPlugin', () => { expect(typeof injectSentryInitPlugin.transform).toBe('function'); }); - describe('tansform', () => { - jest.spyOn(fs, 'existsSync').mockReturnValue(true); - + describe('transform', () => { it('transforms the server index file', () => { const code = 'foo();'; const id = '/node_modules/@sveltejs/kit/src/runtime/server/index.js'; diff --git a/packages/sveltekit/test/server/handleError.test.ts b/packages/sveltekit/test/server/handleError.test.ts index 41e587dfa94c..da73ca0eef5d 100644 --- a/packages/sveltekit/test/server/handleError.test.ts +++ b/packages/sveltekit/test/server/handleError.test.ts @@ -4,14 +4,15 @@ import { Scope } from '@sentry/node'; // adding a custom resolver, which will take too much time. // eslint-disable-next-line import/no-unresolved import type { HandleServerError, RequestEvent } from '@sveltejs/kit'; +import { vi } from 'vitest'; import { handleErrorWithSentry } from '../../src/server/handleError'; -const mockCaptureException = jest.fn(); +const mockCaptureException = vi.fn(); let mockScope = new Scope(); -jest.mock('@sentry/node', () => { - const original = jest.requireActual('@sentry/core'); +vi.mock('@sentry/node', async () => { + const original = (await vi.importActual('@sentry/core')) as any; return { ...original, captureException: (err: unknown, cb: (arg0: unknown) => unknown) => { @@ -22,10 +23,10 @@ jest.mock('@sentry/node', () => { }; }); -const mockAddExceptionMechanism = jest.fn(); +const mockAddExceptionMechanism = vi.fn(); -jest.mock('@sentry/utils', () => { - const original = jest.requireActual('@sentry/utils'); +vi.mock('@sentry/utils', async () => { + const original = (await vi.importActual('@sentry/utils')) as any; return { ...original, addExceptionMechanism: (...args: unknown[]) => mockAddExceptionMechanism(...args), @@ -60,15 +61,15 @@ describe('handleError', () => { it('calls captureException', async () => { const wrappedHandleError = handleErrorWithSentry(handleError); const mockError = new Error('test'); - const returnVal = await wrappedHandleError({ error: mockError, event: requestEvent }); + const returnVal = (await wrappedHandleError({ error: mockError, event: requestEvent })) as any; - expect(returnVal!.message).toEqual('Whoops!'); + expect(returnVal.message).toEqual('Whoops!'); expect(mockCaptureException).toHaveBeenCalledTimes(1); expect(mockCaptureException).toHaveBeenCalledWith(mockError, expect.any(Function)); }); it('adds an exception mechanism', async () => { - const addEventProcessorSpy = jest.spyOn(mockScope, 'addEventProcessor').mockImplementationOnce(callback => { + const addEventProcessorSpy = vi.spyOn(mockScope, 'addEventProcessor').mockImplementationOnce(callback => { void callback({}, { event_id: 'fake-event-id' }); return mockScope; }); diff --git a/packages/sveltekit/test/server/sdk.test.ts b/packages/sveltekit/test/server/sdk.test.ts index 785cf158e5c8..c68be548c91c 100644 --- a/packages/sveltekit/test/server/sdk.test.ts +++ b/packages/sveltekit/test/server/sdk.test.ts @@ -5,12 +5,12 @@ import { GLOBAL_OBJ } from '@sentry/utils'; import { init } from '../../src/server/sdk'; -const nodeInit = jest.spyOn(SentryNode, 'init'); +const nodeInit = vi.spyOn(SentryNode, 'init'); describe('Sentry server SDK', () => { describe('init', () => { afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); GLOBAL_OBJ.__SENTRY__.hub = undefined; }); diff --git a/packages/sveltekit/tsconfig.json b/packages/sveltekit/tsconfig.json index bf45a09f2d71..5282294db1db 100644 --- a/packages/sveltekit/tsconfig.json +++ b/packages/sveltekit/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.json", - "include": ["src/**/*"], + "include": ["src/**/*", "vite.config.ts"], "compilerOptions": { // package-specific options diff --git a/packages/sveltekit/tsconfig.test.json b/packages/sveltekit/tsconfig.test.json index 39a2d7203902..ef8fa4c5130d 100644 --- a/packages/sveltekit/tsconfig.test.json +++ b/packages/sveltekit/tsconfig.test.json @@ -5,6 +5,6 @@ "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] + "types": ["node", "vitest/globals"] } } diff --git a/packages/sveltekit/vite.config.ts b/packages/sveltekit/vite.config.ts new file mode 100644 index 000000000000..f479704b7591 --- /dev/null +++ b/packages/sveltekit/vite.config.ts @@ -0,0 +1,3 @@ +import baseConfig from '../../vite/vite.config'; + +export default baseConfig; diff --git a/tsconfig.dev.json b/tsconfig.dev.json index 8448d6c7a046..8fa9976f16af 100644 --- a/tsconfig.dev.json +++ b/tsconfig.dev.json @@ -2,9 +2,9 @@ { "extends": "./tsconfig.json", - "include": ["**/scripts/**/*.ts", "jest/**/*.ts"], + "include": ["**/scripts/**/*.ts", "jest/**/*.ts", "vite/**/*.ts"], "compilerOptions": { - "types": ["node", "jest"], + "types": ["node", "jest", "vitest/globals"] } } diff --git a/vite/vite.config.ts b/vite/vite.config.ts new file mode 100644 index 000000000000..a5be84382388 --- /dev/null +++ b/vite/vite.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + define: { + __DEBUG_BUILD__: true, + }, + test: { + globals: true, + coverage: { + enabled: true, + reportsDirectory: './coverage', + }, + typecheck: { + tsconfig: './tsconfig.test.json', + }, + }, +}); diff --git a/yarn.lock b/yarn.lock index 3502d7f6c2ac..2568164fa859 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2805,7 +2805,7 @@ js-yaml "^3.13.1" resolve-from "^5.0.0" -"@istanbuljs/schema@^0.1.2": +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== @@ -3029,7 +3029,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.0", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.0", "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== @@ -4404,11 +4404,23 @@ dependencies: "@types/chai" "*" +"@types/chai-subset@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@types/chai-subset/-/chai-subset-1.3.3.tgz#97893814e92abd2c534de422cb377e0e0bdaac94" + integrity sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw== + dependencies: + "@types/chai" "*" + "@types/chai@*", "@types/chai@^4.1.3", "@types/chai@^4.2.9": version "4.2.15" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.15.tgz#b7a6d263c2cecf44b6de9a051cf496249b154553" integrity sha512-rYff6FI+ZTKAPkJUoyz7Udq3GaoDZnxYDEvdEdFZASiA7PoErltHezDishqQiSDWrGxvxmplH304jyzQmjp0AQ== +"@types/chai@^4.3.4": + version "4.3.4" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.4.tgz#e913e8175db8307d78b4e8fa690408ba6b65dee4" + integrity sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw== + "@types/connect@*": version "3.4.34" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" @@ -5288,6 +5300,51 @@ "@typescript-eslint/types" "5.48.0" eslint-visitor-keys "^3.3.0" +"@vitest/coverage-c8@^0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@vitest/coverage-c8/-/coverage-c8-0.29.2.tgz#30b81e32ff11c20e2f3ab78c84e21b4c6c08190c" + integrity sha512-NmD3WirQCeQjjKfHu4iEq18DVOBFbLn9TKVdMpyi5YW2EtnS+K22/WE+9/wRrepOhyeTxuEFgxUVkCAE1GhbnQ== + dependencies: + c8 "^7.13.0" + picocolors "^1.0.0" + std-env "^3.3.1" + +"@vitest/expect@0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-0.29.2.tgz#7503aabd72764612b0bc8258bafa3232ccb81586" + integrity sha512-wjrdHB2ANTch3XKRhjWZN0UueFocH0cQbi2tR5Jtq60Nb3YOSmakjdAvUa2JFBu/o8Vjhj5cYbcMXkZxn1NzmA== + dependencies: + "@vitest/spy" "0.29.2" + "@vitest/utils" "0.29.2" + chai "^4.3.7" + +"@vitest/runner@0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-0.29.2.tgz#bbc7b239758de4158392bb343e48ee5a4aa507e1" + integrity sha512-A1P65f5+6ru36AyHWORhuQBJrOOcmDuhzl5RsaMNFe2jEkoj0faEszQS4CtPU/LxUYVIazlUtZTY0OEZmyZBnA== + dependencies: + "@vitest/utils" "0.29.2" + p-limit "^4.0.0" + pathe "^1.1.0" + +"@vitest/spy@0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-0.29.2.tgz#4210d844fabd9a68a1d2932d6a26c051bd089021" + integrity sha512-Hc44ft5kaAytlGL2PyFwdAsufjbdOvHklwjNy/gy/saRbg9Kfkxfh+PklLm1H2Ib/p586RkQeNFKYuJInUssyw== + dependencies: + tinyspy "^1.0.2" + +"@vitest/utils@0.29.2": + version "0.29.2" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-0.29.2.tgz#8990794a6855de19b59da80413dc5a1e1991da4d" + integrity sha512-F14/Uc+vCdclStS2KEoXJlOLAEyqRhnw0gM27iXw9bMTcyKRPJrQ+rlC6XZ125GIPvvKYMPpVxNhiou6PsEeYQ== + dependencies: + cli-truncate "^3.1.0" + diff "^5.1.0" + loupe "^2.3.6" + picocolors "^1.0.0" + pretty-format "^27.5.1" + "@vue/compiler-core@3.2.45": version "3.2.45" resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz#d9311207d96f6ebd5f4660be129fb99f01ddb41b" @@ -5770,7 +5827,7 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^8.0.0, acorn-walk@^8.1.1: +acorn-walk@^8.0.0, acorn-walk@^8.1.1, acorn-walk@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== @@ -5790,6 +5847,11 @@ acorn@^8.0.4, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.0, acorn@^8.7 resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== +acorn@^8.8.1, acorn@^8.8.2: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" @@ -6005,6 +6067,11 @@ ansi-regex@^5.0.0, ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -6029,6 +6096,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + ansi-to-html@^0.6.15, ansi-to-html@^0.6.6: version "0.6.15" resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.15.tgz#ac6ad4798a00f6aa045535d7f6a9cb9294eebea7" @@ -8448,6 +8520,29 @@ bytes@3.1.2, bytes@^3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +c8@^7.13.0: + version "7.13.0" + resolved "https://registry.yarnpkg.com/c8/-/c8-7.13.0.tgz#a2a70a851278709df5a9247d62d7f3d4bcb5f2e4" + integrity sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@istanbuljs/schema" "^0.1.3" + find-up "^5.0.0" + foreground-child "^2.0.0" + istanbul-lib-coverage "^3.2.0" + istanbul-lib-report "^3.0.0" + istanbul-reports "^3.1.4" + rimraf "^3.0.2" + test-exclude "^6.0.0" + v8-to-istanbul "^9.0.0" + yargs "^16.2.0" + yargs-parser "^20.2.9" + +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + cacache@15.0.5: version "15.0.5" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" @@ -8747,6 +8842,19 @@ chai@^4.1.2: pathval "^1.1.1" type-detect "^4.0.5" +chai@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" + integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^4.1.2" + get-func-name "^2.0.0" + loupe "^2.3.1" + pathval "^1.1.1" + type-detect "^4.0.5" + chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -9019,6 +9127,14 @@ cli-table@^0.3.1: dependencies: colors "1.0.3" +cli-truncate@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" + integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== + dependencies: + slice-ansi "^5.0.0" + string-width "^5.0.0" + cli-width@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" @@ -10402,6 +10518,13 @@ deep-eql@^3.0.1: dependencies: type-detect "^4.0.0" +deep-eql@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== + dependencies: + type-detect "^4.0.0" + deep-equal@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" @@ -10947,6 +11070,11 @@ duplexify@^4.0.0, duplexify@^4.1.1: readable-stream "^3.1.1" stream-shift "^1.0.0" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -11693,6 +11821,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" @@ -12057,7 +12190,7 @@ esbuild@0.13.8: esbuild-windows-64 "0.13.8" esbuild-windows-arm64 "0.13.8" -esbuild@^0.16.3: +esbuild@^0.16.14, esbuild@^0.16.3: version "0.16.17" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.17.tgz#fc2c3914c57ee750635fee71b89f615f25065259" integrity sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg== @@ -13230,6 +13363,14 @@ foreach@^2.0.5: resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^3.0.2" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -15314,6 +15455,11 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + is-generator-fn@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" @@ -15750,6 +15896,14 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +istanbul-reports@^3.1.4: + version "3.1.5" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + istanbul@0.4.5, istanbul@^0.4.0: version "0.4.5" resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" @@ -16503,7 +16657,7 @@ jsonc-parser@3.0.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== -jsonc-parser@3.2.0, jsonc-parser@^3.0.0: +jsonc-parser@3.2.0, jsonc-parser@^3.0.0, jsonc-parser@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== @@ -17221,6 +17375,11 @@ loader.js@~4.7.0: resolved "https://registry.yarnpkg.com/loader.js/-/loader.js-4.7.0.tgz#a1a52902001c83631efde9688b8ab3799325ef1f" integrity sha512-9M2KvGT6duzGMgkOcTkWb+PR/Q2Oe54df/tLgHGVmFpAmtqJ553xJh6N63iFYI2yjo2PeJXbS5skHi/QpJq4vA== +local-pkg@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.4.3.tgz#0ff361ab3ae7f1c19113d9bb97b98b905dbc4963" + integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g== + localforage@^1.8.1: version "1.9.0" resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.9.0.tgz#f3e4d32a8300b362b4634cc4e066d9d00d2f09d1" @@ -17562,6 +17721,13 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +loupe@^2.3.1, loupe@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" + integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== + dependencies: + get-func-name "^2.0.0" + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -18358,6 +18524,16 @@ mktemp@~0.4.0: resolved "https://registry.yarnpkg.com/mktemp/-/mktemp-0.4.0.tgz#6d0515611c8a8c84e484aa2000129b98e981ff0b" integrity sha1-bQUVYRyKjITkhKogABKbmOmB/ws= +mlly@^1.1.0, mlly@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.2.0.tgz#f0f6c2fc8d2d12ea6907cd869066689b5031b613" + integrity sha512-+c7A3CV0KGdKcylsI6khWyts/CYrGTrRVo4R/I7u/cUsy0Conxa6LUhiEzVKIw14lc2L5aiO4+SeVe4TeGRKww== + dependencies: + acorn "^8.8.2" + pathe "^1.1.0" + pkg-types "^1.0.2" + ufo "^1.1.1" + mocha@^6.1.4, mocha@^6.2.0: version "6.2.3" resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.3.tgz#e648432181d8b99393410212664450a4c1e31912" @@ -20442,6 +20618,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pathe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.0.tgz#e2e13f6c62b31a3289af4ba19886c230f295ec03" + integrity sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w== + pathval@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" @@ -20627,6 +20808,15 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-types@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.2.tgz#c233efc5210a781e160e0cafd60c0d0510a4b12e" + integrity sha512-hM58GKXOcj8WTqUXnsQyJYXdeAPbythQgEF3nTcEo+nkD49chjQ9IKm/QJy9xf6JakXptz86h7ecP2024rrLaQ== + dependencies: + jsonc-parser "^3.2.0" + mlly "^1.1.1" + pathe "^1.1.0" + pkg-up@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" @@ -21687,7 +21877,7 @@ postcss@^8.1.10, postcss@^8.1.7, postcss@^8.2.15: picocolors "^1.0.0" source-map-js "^1.0.2" -postcss@^8.2.4, postcss@^8.3.5, postcss@^8.3.7, postcss@^8.4.19: +postcss@^8.2.4, postcss@^8.3.5, postcss@^8.3.7, postcss@^8.4.19, postcss@^8.4.21: version "8.4.21" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== @@ -23284,6 +23474,13 @@ rollup@^2.45.1: optionalDependencies: fsevents "~2.3.2" +rollup@^3.10.0: + version "3.19.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.19.1.tgz#2b3a31ac1ff9f3afab2e523fa687fef5b0ee20fc" + integrity sha512-lAbrdN7neYCg/8WaoWn/ckzCtz+jr70GFfYdlf50OF7387HTg+wiuiqJRFYawwSPpqfqDNYqK7smY/ks2iAudg== + optionalDependencies: + fsevents "~2.3.2" + rollup@^3.7.0: version "3.18.0" resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.18.0.tgz#2354ba63ba66d6a09c652c3ea0dbcd9dad72bbde" @@ -23794,6 +23991,11 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -23923,6 +24125,14 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + smart-buffer@^4.1.0, smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" @@ -24456,6 +24666,11 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + stacktrace-parser@0.1.10, stacktrace-parser@^0.1.10: version "0.1.10" resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" @@ -24488,6 +24703,11 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +std-env@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.3.2.tgz#af27343b001616015534292178327b202b9ee955" + integrity sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA== + stream-browserify@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" @@ -24647,6 +24867,15 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.matchall@^4.0.5, string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" @@ -24751,6 +24980,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" + integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -24802,6 +25038,13 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-literal@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-1.0.1.tgz#0115a332710c849b4e46497891fb8d585e404bd2" + integrity sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q== + dependencies: + acorn "^8.8.2" + strong-log-transformer@2.1.0, strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -25503,6 +25746,21 @@ tiny-warning@^1.0.0: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== +tinybench@^2.3.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.4.0.tgz#83f60d9e5545353610fe7993bd783120bc20c7a7" + integrity sha512-iyziEiyFxX4kyxSp+MtY1oCH/lvjH3PxFN8PGCDeqcZWAJ/i+9y+nL85w99PxVzrIvew/GSkSbDYtiGVa85Afg== + +tinypool@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.3.1.tgz#a99c2e446aba9be05d3e1cb756d6aed7af4723b6" + integrity sha512-zLA1ZXlstbU2rlpA4CIeVaqvWq41MTWqLY3FfsAXgC8+f7Pk7zroaJQxDgxn1xNudKW6Kmj4808rPFShUlIRmQ== + +tinyspy@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-1.1.1.tgz#0cb91d5157892af38cb2d217f5c7e8507a5bf092" + integrity sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g== + tmp@0.0.28: version "0.0.28" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.28.tgz#172735b7f614ea7af39664fa84cf0de4e515d120" @@ -26022,6 +26280,11 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== +ufo@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.1.tgz#e70265e7152f3aba425bd013d150b2cdf4056d7c" + integrity sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg== + uglify-js@^3.1.4: version "3.13.3" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.3.tgz#ce72a1ad154348ea2af61f50933c76cc8802276e" @@ -26441,6 +26704,15 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" +v8-to-istanbul@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + validate-npm-package-license@3.0.4, validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -26518,6 +26790,18 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vite-node@0.29.2: + version "0.29.2" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.29.2.tgz#463626197e248971774075faf3d6896c29cf8062" + integrity sha512-5oe1z6wzI3gkvc4yOBbDBbgpiWiApvuN4P55E8OI131JGrSuo4X3SOZrNmZYo4R8Zkze/dhi572blX0zc+6SdA== + dependencies: + cac "^6.7.14" + debug "^4.3.4" + mlly "^1.1.0" + pathe "^1.1.0" + picocolors "^1.0.0" + vite "^3.0.0 || ^4.0.0" + vite@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/vite/-/vite-4.0.0.tgz#b81b88349a06b2faaa53ae14cf96c942548e3454" @@ -26530,11 +26814,53 @@ vite@4.0.0: optionalDependencies: fsevents "~2.3.2" +"vite@^3.0.0 || ^4.0.0": + version "4.1.4" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.1.4.tgz#170d93bcff97e0ebc09764c053eebe130bfe6ca0" + integrity sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg== + dependencies: + esbuild "^0.16.14" + postcss "^8.4.21" + resolve "^1.22.1" + rollup "^3.10.0" + optionalDependencies: + fsevents "~2.3.2" + vitefu@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.4.tgz#212dc1a9d0254afe65e579351bed4e25d81e0b35" integrity sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g== +vitest@^0.29.2: + version "0.29.2" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.29.2.tgz#0376b547169ddefbde3fbc040b48569ec61d6179" + integrity sha512-ydK9IGbAvoY8wkg29DQ4ivcVviCaUi3ivuPKfZEVddMTenFHUfB8EEDXQV8+RasEk1ACFLgMUqAaDuQ/Nk+mQA== + dependencies: + "@types/chai" "^4.3.4" + "@types/chai-subset" "^1.3.3" + "@types/node" "*" + "@vitest/expect" "0.29.2" + "@vitest/runner" "0.29.2" + "@vitest/spy" "0.29.2" + "@vitest/utils" "0.29.2" + acorn "^8.8.1" + acorn-walk "^8.2.0" + cac "^6.7.14" + chai "^4.3.7" + debug "^4.3.4" + local-pkg "^0.4.2" + pathe "^1.1.0" + picocolors "^1.0.0" + source-map "^0.6.1" + std-env "^3.3.1" + strip-literal "^1.0.0" + tinybench "^2.3.1" + tinypool "^0.3.1" + tinyspy "^1.0.2" + vite "^3.0.0 || ^4.0.0" + vite-node "0.29.2" + why-is-node-running "^2.2.2" + vm-browserify@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" @@ -27183,6 +27509,14 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +why-is-node-running@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.2.2.tgz#4185b2b4699117819e7154594271e7e344c9973e" + integrity sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + wide-align@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" @@ -27483,7 +27817,7 @@ yargs-parser@20.2.4: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: +yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3, yargs-parser@^20.2.9: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== From 4bff5a9aafe973506bd76884f51e1084f79e1afc Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 14 Mar 2023 10:29:33 +0100 Subject: [PATCH 03/54] feat(bundles): Ensure CDN bundles always have a `Replay` export (#7414) --- package.json | 2 + packages/browser/package.json | 1 + packages/browser/rollup.bundle.config.js | 1 + packages/integration-shims/.eslintrc.js | 8 ++ packages/integration-shims/LICENSE | 14 +++ packages/integration-shims/README.md | 9 ++ packages/integration-shims/package.json | 46 +++++++++ .../integration-shims/rollup.npm.config.js | 7 ++ packages/integration-shims/src/Replay.ts | 46 +++++++++ packages/integration-shims/src/index.ts | 1 + packages/integration-shims/tsconfig.json | 11 +++ .../integration-shims/tsconfig.types.json | 10 ++ .../suites/replay/replayShim/init.js | 22 +++++ .../suites/replay/replayShim/template.html | 9 ++ .../suites/replay/replayShim/test.ts | 37 ++++++++ packages/tracing/package.json | 1 + packages/tracing/rollup.bundle.config.js | 1 + packages/tracing/src/index.bundle.base.ts | 92 ++++++++++++++++++ packages/tracing/src/index.bundle.replay.ts | 2 +- packages/tracing/src/index.bundle.ts | 94 +------------------ rollup/bundleHelpers.js | 9 ++ rollup/plugins/bundlePlugins.js | 14 +++ yarn.lock | 22 +++++ 23 files changed, 367 insertions(+), 92 deletions(-) create mode 100644 packages/integration-shims/.eslintrc.js create mode 100644 packages/integration-shims/LICENSE create mode 100644 packages/integration-shims/README.md create mode 100644 packages/integration-shims/package.json create mode 100644 packages/integration-shims/rollup.npm.config.js create mode 100644 packages/integration-shims/src/Replay.ts create mode 100644 packages/integration-shims/src/index.ts create mode 100644 packages/integration-shims/tsconfig.json create mode 100644 packages/integration-shims/tsconfig.types.json create mode 100644 packages/integration-tests/suites/replay/replayShim/init.js create mode 100644 packages/integration-tests/suites/replay/replayShim/template.html create mode 100644 packages/integration-tests/suites/replay/replayShim/test.ts create mode 100644 packages/tracing/src/index.bundle.base.ts diff --git a/package.json b/package.json index dda4cf8e57e2..3fb01dada5ed 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "packages/hub", "packages/integration-tests", "packages/integrations", + "packages/integration-shims", "packages/overhead-metrics", "packages/nextjs", "packages/node", @@ -105,6 +106,7 @@ "rollup": "^2.67.1", "rollup-plugin-cleanup": "3.2.1", "rollup-plugin-license": "^2.6.1", + "rollup-plugin-modify": "^3.0.0", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.31.2", "sinon": "^7.3.2", diff --git a/packages/browser/package.json b/packages/browser/package.json index 20dc64aad29e..fb2a3ccd2a54 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -23,6 +23,7 @@ "tslib": "^1.9.3" }, "devDependencies": { + "@sentry-internal/integration-shims": "7.43.0", "@types/md5": "2.1.33", "btoa": "^1.2.1", "chai": "^4.1.2", diff --git a/packages/browser/rollup.bundle.config.js b/packages/browser/rollup.bundle.config.js index e063a9bd4ab0..380ca59eed95 100644 --- a/packages/browser/rollup.bundle.config.js +++ b/packages/browser/rollup.bundle.config.js @@ -8,6 +8,7 @@ const builds = []; entrypoints: ['src/index.ts'], jsVersion, licenseTitle: '@sentry/browser', + includeReplay: 'shim', outputFileBase: () => `bundles/bundle${jsVersion === 'es5' ? '.es5' : ''}`, }); diff --git a/packages/integration-shims/.eslintrc.js b/packages/integration-shims/.eslintrc.js new file mode 100644 index 000000000000..0c83f7d0ff9d --- /dev/null +++ b/packages/integration-shims/.eslintrc.js @@ -0,0 +1,8 @@ +// Note: All paths are relative to the directory in which eslint is being run, rather than the directory where this file +// lives + +// ESLint config docs: https://eslint.org/docs/user-guide/configuring/ + +module.exports = { + extends: ['../../.eslintrc.js'], +}; diff --git a/packages/integration-shims/LICENSE b/packages/integration-shims/LICENSE new file mode 100644 index 000000000000..d11896ba1181 --- /dev/null +++ b/packages/integration-shims/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2023 Sentry (https://sentry.io) and individual contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/integration-shims/README.md b/packages/integration-shims/README.md new file mode 100644 index 000000000000..523e3e4fd85f --- /dev/null +++ b/packages/integration-shims/README.md @@ -0,0 +1,9 @@ +

+ + Sentry + +

+ +# Sentry Integration Shims + +This internal package exports shims for Integrations, which are used in order to guarantee a consistent CDN bundle output. diff --git a/packages/integration-shims/package.json b/packages/integration-shims/package.json new file mode 100644 index 000000000000..e986841e3129 --- /dev/null +++ b/packages/integration-shims/package.json @@ -0,0 +1,46 @@ +{ + "name": "@sentry-internal/integration-shims", + "version": "7.43.0", + "description": "Shims for integrations in Sentry SDK.", + "main": "build/cjs/index.js", + "module": "build/esm/index.js", + "types": "build/types/index.d.ts", + "sideEffects": false, + "private": true, + "scripts": { + "build": "run-p build:transpile build:types", + "build:transpile": "rollup -c rollup.npm.config.js", + "build:types": "tsc -p tsconfig.types.json", + "build:dev": "yarn build", + "build:watch": "run-p build:transpile:watch build:types:watch", + "build:dev:watch": "run-p build:watch", + "build:transpile:watch": "yarn build:rollup --watch", + "build:types:watch": "yarn build:types --watch", + "clean": "rimraf build", + "fix": "run-s fix:eslint fix:prettier", + "fix:eslint": "eslint . --format stylish --fix", + "fix:prettier": "prettier --write \"{src,test}/**/*.ts\"", + "lint": "run-s lint:prettier lint:eslint", + "lint:eslint": "eslint . --format stylish", + "lint:prettier": "prettier --check \"{src,test}/**/*.ts\"" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/getsentry/sentry-javascript.git" + }, + "author": "Sentry", + "license": "MIT", + "bugs": { + "url": "https://github.com/getsentry/sentry-javascript/issues" + }, + "devDependencies": {}, + "dependencies": { + "@sentry/types": "7.43.0" + }, + "engines": { + "node": ">=12" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/packages/integration-shims/rollup.npm.config.js b/packages/integration-shims/rollup.npm.config.js new file mode 100644 index 000000000000..2928d05abeed --- /dev/null +++ b/packages/integration-shims/rollup.npm.config.js @@ -0,0 +1,7 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants( + makeBaseNPMConfig({ + entrypoints: ['src/index.ts'], + }), +); diff --git a/packages/integration-shims/src/Replay.ts b/packages/integration-shims/src/Replay.ts new file mode 100644 index 000000000000..c89bbd415d5f --- /dev/null +++ b/packages/integration-shims/src/Replay.ts @@ -0,0 +1,46 @@ +import type { Integration } from '@sentry/types'; + +/** + * This is a shim for the Replay integration. + * It is needed in order for the CDN bundles to continue working when users add/remove replay + * from it, without changing their config. This is necessary for the loader mechanism. + */ +class ReplayShim implements Integration { + /** + * @inheritDoc + */ + public static id: string = 'Replay'; + + /** + * @inheritDoc + */ + public name: string = ReplayShim.id; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public constructor(_options: any) { + // eslint-disable-next-line no-console + console.error('You are using new Replay() even though this bundle does not include the replay integration.'); + } + + /** jsdoc */ + public setupOnce(): void { + // noop + } + + /** jsdoc */ + public start(): void { + // noop + } + + /** jsdoc */ + public stop(): void { + // noop + } + + /** jsdoc */ + public flush(): void { + // noop + } +} + +export { ReplayShim as Replay }; diff --git a/packages/integration-shims/src/index.ts b/packages/integration-shims/src/index.ts new file mode 100644 index 000000000000..a55c52bcb483 --- /dev/null +++ b/packages/integration-shims/src/index.ts @@ -0,0 +1 @@ +export * from './Replay'; diff --git a/packages/integration-shims/tsconfig.json b/packages/integration-shims/tsconfig.json new file mode 100644 index 000000000000..dd48230408ab --- /dev/null +++ b/packages/integration-shims/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "esnext", + "lib": ["ES6"], + "esModuleInterop": true, + "target": "es6", + "strictPropertyInitialization": false + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/integration-shims/tsconfig.types.json b/packages/integration-shims/tsconfig.types.json new file mode 100644 index 000000000000..16be672259fb --- /dev/null +++ b/packages/integration-shims/tsconfig.types.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/index.ts"], + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "build/types" + } +} diff --git a/packages/integration-tests/suites/replay/replayShim/init.js b/packages/integration-tests/suites/replay/replayShim/init.js new file mode 100644 index 000000000000..b53ada8b5e7c --- /dev/null +++ b/packages/integration-tests/suites/replay/replayShim/init.js @@ -0,0 +1,22 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +// Replay should not actually work, but still not error out +window.Replay = new Sentry.Replay({ + flushMinDelay: 200, + flushMaxDelay: 200, +}); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 1, + replaysSessionSampleRate: 1.0, + replaysOnErrorSampleRate: 0.0, + integrations: [window.Replay], +}); + +// Ensure none of these break +window.Replay.start(); +window.Replay.stop(); +window.Replay.flush(); diff --git a/packages/integration-tests/suites/replay/replayShim/template.html b/packages/integration-tests/suites/replay/replayShim/template.html new file mode 100644 index 000000000000..2b3e2f0b27b4 --- /dev/null +++ b/packages/integration-tests/suites/replay/replayShim/template.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/integration-tests/suites/replay/replayShim/test.ts b/packages/integration-tests/suites/replay/replayShim/test.ts new file mode 100644 index 000000000000..ca11e1c431f2 --- /dev/null +++ b/packages/integration-tests/suites/replay/replayShim/test.ts @@ -0,0 +1,37 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../utils/fixtures'; + +sentryTest( + 'exports a shim Replay integration for non-replay bundles', + async ({ getLocalTestPath, page, forceFlushReplay }) => { + const bundle = process.env.PW_BUNDLE; + + if (!bundle || !bundle.startsWith('bundle_') || bundle.includes('replay')) { + sentryTest.skip(); + } + + const consoleMessages: string[] = []; + page.on('console', msg => consoleMessages.push(msg.text())); + + let requestCount = 0; + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + requestCount++; + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.goto(url); + await forceFlushReplay(); + + expect(requestCount).toBe(0); + expect(consoleMessages).toEqual([ + 'You are using new Replay() even though this bundle does not include the replay integration.', + ]); + }, +); diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 8ecb03f572e8..5ac7ac899983 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -23,6 +23,7 @@ }, "devDependencies": { "@sentry/browser": "7.43.0", + "@sentry-internal/integration-shims": "7.43.0", "@types/express": "^4.17.14" }, "scripts": { diff --git a/packages/tracing/rollup.bundle.config.js b/packages/tracing/rollup.bundle.config.js index a0c5bc6cd421..52aa6396a370 100644 --- a/packages/tracing/rollup.bundle.config.js +++ b/packages/tracing/rollup.bundle.config.js @@ -8,6 +8,7 @@ const builds = []; entrypoints: ['src/index.bundle.ts'], jsVersion, licenseTitle: '@sentry/tracing & @sentry/browser', + includeReplay: false, outputFileBase: () => `bundles/bundle.tracing${jsVersion === 'es5' ? '.es5' : ''}`, }); diff --git a/packages/tracing/src/index.bundle.base.ts b/packages/tracing/src/index.bundle.base.ts new file mode 100644 index 000000000000..a753bab932d4 --- /dev/null +++ b/packages/tracing/src/index.bundle.base.ts @@ -0,0 +1,92 @@ +export type { + Breadcrumb, + Request, + SdkInfo, + Event, + Exception, + // eslint-disable-next-line deprecation/deprecation + Severity, + SeverityLevel, + StackFrame, + Stacktrace, + Thread, + User, +} from '@sentry/types'; + +export type { BrowserOptions, ReportDialogOptions } from '@sentry/browser'; + +export { + addGlobalEventProcessor, + addBreadcrumb, + captureException, + captureEvent, + captureMessage, + configureScope, + getHubFromCarrier, + getCurrentHub, + Hub, + Scope, + setContext, + setExtra, + setExtras, + setTag, + setTags, + setUser, + startTransaction, + makeFetchTransport, + makeXHRTransport, + withScope, +} from '@sentry/browser'; + +export { BrowserClient } from '@sentry/browser'; +export { + defaultIntegrations, + defaultStackParser, + forceLoad, + init, + lastEventId, + onLoad, + showReportDialog, + flush, + close, + wrap, +} from '@sentry/browser'; +export { SDK_VERSION } from '@sentry/browser'; + +import { Integrations as BrowserIntegrations } from '@sentry/browser'; +import type { Integration } from '@sentry/types'; +import { GLOBAL_OBJ } from '@sentry/utils'; + +import { BrowserTracing } from './browser'; +import { addExtensionMethods } from './extensions'; + +export { Span } from '@sentry/core'; + +let windowIntegrations = {}; + +// This block is needed to add compatibility with the integrations packages when used with a CDN +if (GLOBAL_OBJ.Sentry && GLOBAL_OBJ.Sentry.Integrations) { + windowIntegrations = GLOBAL_OBJ.Sentry.Integrations; +} + +// For whatever reason, it does not recognize BrowserTracing or some of the BrowserIntegrations as Integration +const INTEGRATIONS: Record< + string, + Integration | typeof BrowserTracing | typeof BrowserIntegrations[keyof typeof BrowserIntegrations] +> = { + ...windowIntegrations, + ...BrowserIntegrations, + BrowserTracing, +}; + +export { INTEGRATIONS as Integrations }; +// Though in this case exporting `BrowserTracing` separately (in addition to exporting it as part of +// `Sentry.Integrations`) doesn't gain us any bundle size advantage (we're making the bundle here, not the user, and we +// can't leave anything out of ours), it does bring the API for using the integration in line with that recommended for +// users bundling Sentry themselves. +export { BrowserTracing }; + +// We are patching the global object with our hub extension methods +addExtensionMethods(); + +export { addExtensionMethods }; diff --git a/packages/tracing/src/index.bundle.replay.ts b/packages/tracing/src/index.bundle.replay.ts index 403cc6342b65..fe503b961461 100644 --- a/packages/tracing/src/index.bundle.replay.ts +++ b/packages/tracing/src/index.bundle.replay.ts @@ -9,4 +9,4 @@ Sentry.Integrations.Replay = Replay; export { Replay }; -export * from './index.bundle'; +export * from './index.bundle.base'; diff --git a/packages/tracing/src/index.bundle.ts b/packages/tracing/src/index.bundle.ts index a753bab932d4..ac897b145298 100644 --- a/packages/tracing/src/index.bundle.ts +++ b/packages/tracing/src/index.bundle.ts @@ -1,92 +1,4 @@ -export type { - Breadcrumb, - Request, - SdkInfo, - Event, - Exception, - // eslint-disable-next-line deprecation/deprecation - Severity, - SeverityLevel, - StackFrame, - Stacktrace, - Thread, - User, -} from '@sentry/types'; +// This is exported so the loader does not fail when switching off Replay +export { Replay } from '@sentry-internal/integration-shims'; -export type { BrowserOptions, ReportDialogOptions } from '@sentry/browser'; - -export { - addGlobalEventProcessor, - addBreadcrumb, - captureException, - captureEvent, - captureMessage, - configureScope, - getHubFromCarrier, - getCurrentHub, - Hub, - Scope, - setContext, - setExtra, - setExtras, - setTag, - setTags, - setUser, - startTransaction, - makeFetchTransport, - makeXHRTransport, - withScope, -} from '@sentry/browser'; - -export { BrowserClient } from '@sentry/browser'; -export { - defaultIntegrations, - defaultStackParser, - forceLoad, - init, - lastEventId, - onLoad, - showReportDialog, - flush, - close, - wrap, -} from '@sentry/browser'; -export { SDK_VERSION } from '@sentry/browser'; - -import { Integrations as BrowserIntegrations } from '@sentry/browser'; -import type { Integration } from '@sentry/types'; -import { GLOBAL_OBJ } from '@sentry/utils'; - -import { BrowserTracing } from './browser'; -import { addExtensionMethods } from './extensions'; - -export { Span } from '@sentry/core'; - -let windowIntegrations = {}; - -// This block is needed to add compatibility with the integrations packages when used with a CDN -if (GLOBAL_OBJ.Sentry && GLOBAL_OBJ.Sentry.Integrations) { - windowIntegrations = GLOBAL_OBJ.Sentry.Integrations; -} - -// For whatever reason, it does not recognize BrowserTracing or some of the BrowserIntegrations as Integration -const INTEGRATIONS: Record< - string, - Integration | typeof BrowserTracing | typeof BrowserIntegrations[keyof typeof BrowserIntegrations] -> = { - ...windowIntegrations, - ...BrowserIntegrations, - BrowserTracing, -}; - -export { INTEGRATIONS as Integrations }; -// Though in this case exporting `BrowserTracing` separately (in addition to exporting it as part of -// `Sentry.Integrations`) doesn't gain us any bundle size advantage (we're making the bundle here, not the user, and we -// can't leave anything out of ours), it does bring the API for using the integration in line with that recommended for -// users bundling Sentry themselves. -export { BrowserTracing }; - -// We are patching the global object with our hub extension methods -addExtensionMethods(); - -export { addExtensionMethods }; +export * from './index.bundle.base'; diff --git a/rollup/bundleHelpers.js b/rollup/bundleHelpers.js index 56ef80d12415..e81069990fa4 100644 --- a/rollup/bundleHelpers.js +++ b/rollup/bundleHelpers.js @@ -18,6 +18,7 @@ import { makeTSPlugin, makeExcludeBlockPlugin, makeSetSDKSourcePlugin, + makeReplayShimPlugin, } from './plugins/index.js'; import { mergePlugins } from './utils'; @@ -43,8 +44,10 @@ export function makeBaseBundleConfig(options) { const licensePlugin = makeLicensePlugin(licenseTitle); const tsPlugin = makeTSPlugin(jsVersion.toLowerCase()); const excludeReplayPlugin = makeExcludeBlockPlugin('REPLAY'); + const excludeReplayShimPlugin = makeExcludeBlockPlugin('REPLAY_SHIM'); const excludeOfflineTransport = makeExcludeBlockPlugin('OFFLINE'); const excludeBrowserProfiling = makeExcludeBlockPlugin('BROWSER_PROFILING'); + const replayShimPlugin = makeReplayShimPlugin(); // The `commonjs` plugin is the `esModuleInterop` of the bundling world. When used with `transformMixedEsModules`, it // will include all dependencies, imported or required, in the final bundle. (Without it, CJS modules aren't included @@ -61,6 +64,12 @@ export function makeBaseBundleConfig(options) { plugins: [markAsBrowserBuildPlugin], }; + if (includeReplay === 'shim') { + standAloneBundleConfig.plugins.push(replayShimPlugin); + } else { + standAloneBundleConfig.plugins.push(excludeReplayShimPlugin); + } + if (!includeReplay) { standAloneBundleConfig.plugins.push(excludeReplayPlugin); } diff --git a/rollup/plugins/bundlePlugins.js b/rollup/plugins/bundlePlugins.js index 49dcaefbdf73..f00e090c740c 100644 --- a/rollup/plugins/bundlePlugins.js +++ b/rollup/plugins/bundlePlugins.js @@ -18,6 +18,7 @@ import replace from '@rollup/plugin-replace'; import { terser } from 'rollup-plugin-terser'; import typescript from 'rollup-plugin-typescript2'; import MagicString from 'magic-string'; +import modify from 'rollup-plugin-modify'; /** * Create a plugin to add an identification banner to the top of stand-alone bundles. @@ -215,6 +216,19 @@ export function makeExcludeBlockPlugin(type) { return plugin; } +export function makeReplayShimPlugin() { + // This is designed to replace the re-export in browser/index.ts to export the shim + const plugin = modify({ + find: '@sentry/replay', + replace: '@sentry-internal/integration-shims', + }); + + // give it a nicer name for later, when we'll need to sort the plugins + plugin.name = 'replayShim'; + + return plugin; +} + // We don't pass these plugins any options which need to be calculated or changed by us, so no need to wrap them in // another factory function, as they are themselves already factory functions. export { resolve as makeNodeResolvePlugin }; diff --git a/yarn.lock b/yarn.lock index 2568164fa859..d39f972d5311 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17812,6 +17812,13 @@ madge@4.0.2: typescript "^3.9.5" walkdir "^0.4.1" +magic-string@0.25.2: + version "0.25.2" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.2.tgz#139c3a729515ec55e96e69e82a11fe890a293ad9" + integrity sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg== + dependencies: + sourcemap-codec "^1.4.4" + magic-string@0.25.7: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" @@ -20002,6 +20009,13 @@ osenv@^0.1.3, osenv@^0.1.5: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +ospec@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/ospec/-/ospec-3.1.0.tgz#d36b8e10110f58f63a463df2390a7a73fe9579a8" + integrity sha512-+nGtjV3vlADp+UGfL51miAh/hB4awPBkQrArhcgG4trAaoA2gKt5bf9w0m9ch9zOr555cHWaCHZEDiBOkNZSxw== + dependencies: + glob "^7.1.3" + p-cancelable@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" @@ -23416,6 +23430,14 @@ rollup-plugin-license@^2.6.1: spdx-expression-validate "2.0.0" spdx-satisfies "5.0.1" +rollup-plugin-modify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-modify/-/rollup-plugin-modify-3.0.0.tgz#5326e11dfec247e8bbdd9507f3da1da1e5c7818b" + integrity sha512-p/ffs0Y2jz2dEnWjq1oVC7SY37tuS+aP7whoNaQz1EAAOPg+k3vKJo8cMMWx6xpdd0NzhX4y2YF9o/NPu5YR0Q== + dependencies: + magic-string "0.25.2" + ospec "3.1.0" + rollup-plugin-sourcemaps@^0.6.0, rollup-plugin-sourcemaps@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz#bf93913ffe056e414419607f1d02780d7ece84ed" From 8343eb0d13fa83cbb850d4d026d5ace3a26d1201 Mon Sep 17 00:00:00 2001 From: Ash Anand <0Calories@users.noreply.github.com> Date: Tue, 14 Mar 2023 05:41:45 -0400 Subject: [PATCH 04/54] feat(tracing): Bring back `finishReason` for interaction transactions (#7449) --- packages/core/src/tracing/idletransaction.ts | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/core/src/tracing/idletransaction.ts b/packages/core/src/tracing/idletransaction.ts index 49c7b64ea4a0..48fd2e50356e 100644 --- a/packages/core/src/tracing/idletransaction.ts +++ b/packages/core/src/tracing/idletransaction.ts @@ -13,6 +13,17 @@ export const TRACING_DEFAULTS = { heartbeatInterval: 5000, }; +const FINISH_REASON_TAG = 'finishReason'; + +const IDLE_TRANSACTION_FINISH_REASONS = [ + 'heartbeatFailed', + 'idleTimeout', + 'documentHidden', + 'finalTimeout', + 'externalFinish', + 'cancelled', +]; + /** * @inheritDoc */ @@ -79,6 +90,8 @@ export class IdleTransaction extends Transaction { */ private _idleTimeoutID: ReturnType | undefined; + private _finishReason: typeof IDLE_TRANSACTION_FINISH_REASONS[number] = IDLE_TRANSACTION_FINISH_REASONS[4]; + public constructor( transactionContext: TransactionContext, private readonly _idleHub: Hub, @@ -111,6 +124,7 @@ export class IdleTransaction extends Transaction { setTimeout(() => { if (!this._finished) { this.setStatus('deadline_exceeded'); + this._finishReason = IDLE_TRANSACTION_FINISH_REASONS[3]; this.finish(); } }, this._finalTimeout); @@ -121,6 +135,10 @@ export class IdleTransaction extends Transaction { this._finished = true; this.activities = {}; + if (this.op === 'ui.action.click') { + this.setTag(FINISH_REASON_TAG, this._finishReason); + } + if (this.spanRecorder) { __DEBUG_BUILD__ && logger.log('[Tracing] finishing IdleTransaction', new Date(endTimestamp * 1000).toISOString(), this.op); @@ -227,6 +245,7 @@ export class IdleTransaction extends Transaction { this._idleTimeoutCanceledPermanently = restartOnChildSpanChange === false; if (Object.keys(this.activities).length === 0 && this._idleTimeoutCanceledPermanently) { + this._finishReason = IDLE_TRANSACTION_FINISH_REASONS[5]; this.finish(endTimestamp); } } @@ -239,6 +258,7 @@ export class IdleTransaction extends Transaction { this.cancelIdleTimeout(); this._idleTimeoutID = setTimeout(() => { if (!this._finished && Object.keys(this.activities).length === 0) { + this._finishReason = IDLE_TRANSACTION_FINISH_REASONS[1]; this.finish(endTimestamp); } }, this._idleTimeout); @@ -270,6 +290,7 @@ export class IdleTransaction extends Transaction { if (Object.keys(this.activities).length === 0) { const endTimestamp = timestampWithMs(); if (this._idleTimeoutCanceledPermanently) { + this._finishReason = IDLE_TRANSACTION_FINISH_REASONS[5]; this.finish(endTimestamp); } else { // We need to add the timeout here to have the real endtimestamp of the transaction @@ -302,6 +323,7 @@ export class IdleTransaction extends Transaction { if (this._heartbeatCounter >= 3) { __DEBUG_BUILD__ && logger.log('[Tracing] Transaction finished because of no change for 3 heart beats'); this.setStatus('deadline_exceeded'); + this._finishReason = IDLE_TRANSACTION_FINISH_REASONS[0]; this.finish(); } else { this._pingHeartbeat(); From 45f5c2138c0732593cb56a0d7881cf5e287bd518 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 14 Mar 2023 11:46:19 +0100 Subject: [PATCH 05/54] fix(nextjs): Fix broken server component wrapping because of interrupted promise chain (#7456) --- .../nextjs-app-dir/package.json | 2 +- .../nextjs-app-dir/yarn.lock | 172 +++++++++--------- .../server/wrapServerComponentWithSentry.ts | 10 +- 3 files changed, 93 insertions(+), 91 deletions(-) diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/package.json b/packages/e2e-tests/test-applications/nextjs-app-dir/package.json index 7459a5d03b12..5256c1aa8cdb 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/package.json +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/package.json @@ -16,7 +16,7 @@ "@types/node": "18.11.17", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", - "next": "13.2.3", + "next": "13.2.4", "react": "18.2.0", "react-dom": "18.2.0", "typescript": "4.9.4" diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/yarn.lock b/packages/e2e-tests/test-applications/nextjs-app-dir/yarn.lock index 6a36d48f01f4..c4b81c9fc1d4 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/yarn.lock +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/yarn.lock @@ -27,80 +27,80 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@next/env@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.2.3.tgz#77ca49edb3c1d7c5263bb8f2ebe686080e98279e" - integrity sha512-FN50r/E+b8wuqyRjmGaqvqNDuWBWYWQiigfZ50KnSFH0f+AMQQyaZl+Zm2+CIpKk0fL9QxhLxOpTVA3xFHgFow== +"@next/env@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.2.4.tgz#8b763700262b2445140a44a8c8d088cef676dbae" + integrity sha512-+Mq3TtpkeeKFZanPturjcXt+KHfKYnLlX6jMLyCrmpq6OOs4i1GqBOAauSkii9QeKCMTYzGppar21JU57b/GEA== "@next/font@13.0.7": version "13.0.7" resolved "https://registry.yarnpkg.com/@next/font/-/font-13.0.7.tgz#e0046376edb0ce592d9cfddea8f4ab321eb1515a" integrity sha512-39SzuoMI6jbrIzPs3KtXdKX03OrVp6Y7kRHcoVmOg69spiBzruPJ5x5DQSfN+OXqznbvVBNZBXnmdnSqs3qXiA== -"@next/swc-android-arm-eabi@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.3.tgz#85eed560c87c7996558c868a117be9780778f192" - integrity sha512-mykdVaAXX/gm+eFO2kPeVjnOCKwanJ9mV2U0lsUGLrEdMUifPUjiXKc6qFAIs08PvmTMOLMNnUxqhGsJlWGKSw== - -"@next/swc-android-arm64@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.2.3.tgz#8ac54ca9795a48afc4631b4823a4864bd5db0129" - integrity sha512-8XwHPpA12gdIFtope+n9xCtJZM3U4gH4vVTpUwJ2w1kfxFmCpwQ4xmeGSkR67uOg80yRMuF0h9V1ueo05sws5w== - -"@next/swc-darwin-arm64@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.2.3.tgz#f674e3c65aec505b6d218a662ade3fe248ccdbda" - integrity sha512-TXOubiFdLpMfMtaRu1K5d1I9ipKbW5iS2BNbu8zJhoqrhk3Kp7aRKTxqFfWrbliAHhWVE/3fQZUYZOWSXVQi1w== - -"@next/swc-darwin-x64@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.2.3.tgz#a15ea7fb4c46034a8f5e387906d0cad08387075a" - integrity sha512-GZctkN6bJbpjlFiS5pylgB2pifHvgkqLAPumJzxnxkf7kqNm6rOGuNjsROvOWVWXmKhrzQkREO/WPS2aWsr/yw== - -"@next/swc-freebsd-x64@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.3.tgz#f7ac6ae4f7d706ff2431f33e40230a554c8c2cbc" - integrity sha512-rK6GpmMt/mU6MPuav0/M7hJ/3t8HbKPCELw/Uqhi4732xoq2hJ2zbo2FkYs56y6w0KiXrIp4IOwNB9K8L/q62g== - -"@next/swc-linux-arm-gnueabihf@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.3.tgz#84ad9e9679d55542a23b590ad9f2e1e9b2df62f7" - integrity sha512-yeiCp/Odt1UJ4KUE89XkeaaboIDiVFqKP4esvoLKGJ0fcqJXMofj4ad3tuQxAMs3F+qqrz9MclqhAHkex1aPZA== - -"@next/swc-linux-arm64-gnu@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.2.3.tgz#56f9175bc632d647c60b9e8bedc0875edf92d8b7" - integrity sha512-/miIopDOUsuNlvjBjTipvoyjjaxgkOuvlz+cIbbPcm1eFvzX2ltSfgMgty15GuOiR8Hub4FeTSiq3g2dmCkzGA== - -"@next/swc-linux-arm64-musl@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.2.3.tgz#7d4cf00e8f1729a3de464da0624773f5d0d14888" - integrity sha512-sujxFDhMMDjqhruup8LLGV/y+nCPi6nm5DlFoThMJFvaaKr/imhkXuk8uCTq4YJDbtRxnjydFv2y8laBSJVC2g== - -"@next/swc-linux-x64-gnu@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.2.3.tgz#17de404910c4ebf7a1d366b19334d7e27e126ab0" - integrity sha512-w5MyxPknVvC9LVnMenAYMXMx4KxPwXuJRMQFvY71uXg68n7cvcas85U5zkdrbmuZ+JvsO5SIG8k36/6X3nUhmQ== - -"@next/swc-linux-x64-musl@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.2.3.tgz#07cb7b7f3a3a98034e2533f82638a9b099ba4ab1" - integrity sha512-CTeelh8OzSOVqpzMFMFnVRJIFAFQoTsI9RmVJWW/92S4xfECGcOzgsX37CZ8K982WHRzKU7exeh7vYdG/Eh4CA== - -"@next/swc-win32-arm64-msvc@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.2.3.tgz#b9ac98c954c71ec9de45d3497a8585096b873152" - integrity sha512-7N1KBQP5mo4xf52cFCHgMjzbc9jizIlkTepe9tMa2WFvEIlKDfdt38QYcr9mbtny17yuaIw02FXOVEytGzqdOQ== - -"@next/swc-win32-ia32-msvc@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.2.3.tgz#5ec48653a48fd664e940c69c96bba698fdae92eb" - integrity sha512-LzWD5pTSipUXTEMRjtxES/NBYktuZdo7xExJqGDMnZU8WOI+v9mQzsmQgZS/q02eIv78JOCSemqVVKZBGCgUvA== - -"@next/swc-win32-x64-msvc@13.2.3": - version "13.2.3" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.2.3.tgz#cd432f280beb8d8de5b7cd2501e9f502e9f3dd72" - integrity sha512-aLG2MaFs4y7IwaMTosz2r4mVbqRyCnMoFqOcmfTi7/mAS+G4IMH0vJp4oLdbshqiVoiVuKrAfqtXj55/m7Qu1Q== +"@next/swc-android-arm-eabi@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.4.tgz#758d0403771e549f9cee71cbabc0cb16a6c947c0" + integrity sha512-DWlalTSkLjDU11MY11jg17O1gGQzpRccM9Oes2yTqj2DpHndajrXHGxj9HGtJ+idq2k7ImUdJVWS2h2l/EDJOw== + +"@next/swc-android-arm64@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.2.4.tgz#834d586523045110d5602e0c8aae9028835ac427" + integrity sha512-sRavmUImUCf332Gy+PjIfLkMhiRX1Ez4SI+3vFDRs1N5eXp+uNzjFUK/oLMMOzk6KFSkbiK/3Wt8+dHQR/flNg== + +"@next/swc-darwin-arm64@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.2.4.tgz#5006fca179a36ef3a24d293abadec7438dbb48c6" + integrity sha512-S6vBl+OrInP47TM3LlYx65betocKUUlTZDDKzTiRDbsRESeyIkBtZ6Qi5uT2zQs4imqllJznVjFd1bXLx3Aa6A== + +"@next/swc-darwin-x64@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.2.4.tgz#6549c7c04322766acc3264ccdb3e1b43fcaf7946" + integrity sha512-a6LBuoYGcFOPGd4o8TPo7wmv5FnMr+Prz+vYHopEDuhDoMSHOnC+v+Ab4D7F0NMZkvQjEJQdJS3rqgFhlZmKlw== + +"@next/swc-freebsd-x64@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.4.tgz#0bbe28979e3e868debc2cc06e45e186ce195b7f4" + integrity sha512-kkbzKVZGPaXRBPisoAQkh3xh22r+TD+5HwoC5bOkALraJ0dsOQgSMAvzMXKsN3tMzJUPS0tjtRf1cTzrQ0I5vQ== + +"@next/swc-linux-arm-gnueabihf@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.4.tgz#1d28d2203f5a7427d6e7119d7bcb5fc40959fb3e" + integrity sha512-7qA1++UY0fjprqtjBZaOA6cas/7GekpjVsZn/0uHvquuITFCdKGFCsKNBx3S0Rpxmx6WYo0GcmhNRM9ru08BGg== + +"@next/swc-linux-arm64-gnu@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.2.4.tgz#eb26448190948cdf4c44b8f34110a3ecea32f1d0" + integrity sha512-xzYZdAeq883MwXgcwc72hqo/F/dwUxCukpDOkx/j1HTq/J0wJthMGjinN9wH5bPR98Mfeh1MZJ91WWPnZOedOg== + +"@next/swc-linux-arm64-musl@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.2.4.tgz#c4227c0acd94a420bb14924820710e6284d234d3" + integrity sha512-8rXr3WfmqSiYkb71qzuDP6I6R2T2tpkmf83elDN8z783N9nvTJf2E7eLx86wu2OJCi4T05nuxCsh4IOU3LQ5xw== + +"@next/swc-linux-x64-gnu@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.2.4.tgz#6bcb540944ee9b0209b33bfc23b240c2044dfc3e" + integrity sha512-Ngxh51zGSlYJ4EfpKG4LI6WfquulNdtmHg1yuOYlaAr33KyPJp4HeN/tivBnAHcZkoNy0hh/SbwDyCnz5PFJQQ== + +"@next/swc-linux-x64-musl@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.2.4.tgz#ce21e43251eaf09a09df39372b2c3e38028c30ff" + integrity sha512-gOvwIYoSxd+j14LOcvJr+ekd9fwYT1RyMAHOp7znA10+l40wkFiMONPLWiZuHxfRk+Dy7YdNdDh3ImumvL6VwA== + +"@next/swc-win32-arm64-msvc@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.2.4.tgz#68220063d8e5e082f5465498675640dedb670ff1" + integrity sha512-q3NJzcfClgBm4HvdcnoEncmztxrA5GXqKeiZ/hADvC56pwNALt3ngDC6t6qr1YW9V/EPDxCYeaX4zYxHciW4Dw== + +"@next/swc-win32-ia32-msvc@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.2.4.tgz#7c120ab54a081be9566df310bed834f168252990" + integrity sha512-/eZ5ncmHUYtD2fc6EUmAIZlAJnVT2YmxDsKs1Ourx0ttTtvtma/WKlMV5NoUsyOez0f9ExLyOpeCoz5aj+MPXw== + +"@next/swc-win32-x64-msvc@13.2.4": + version "13.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.2.4.tgz#5abda92fe12b9829bf7951c4a221282c56041144" + integrity sha512-0MffFmyv7tBLlji01qc0IaPP/LVExzvj7/R5x1Jph1bTAIj4Vu81yFQWHHQAP6r4ff9Ukj1mBK6MDNVXm7Tcvw== "@playwright/test@^1.27.1": version "1.31.2" @@ -251,30 +251,30 @@ nanoid@^3.3.4: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== -next@13.2.3: - version "13.2.3" - resolved "https://registry.yarnpkg.com/next/-/next-13.2.3.tgz#92d170e7aca421321f230ff80c35c4751035f42e" - integrity sha512-nKFJC6upCPN7DWRx4+0S/1PIOT7vNlCT157w9AzbXEgKy6zkiPKEt5YyRUsRZkmpEqBVrGgOqNfwecTociyg+w== +next@13.2.4: + version "13.2.4" + resolved "https://registry.yarnpkg.com/next/-/next-13.2.4.tgz#2363330392b0f7da02ab41301f60857ffa7f67d6" + integrity sha512-g1I30317cThkEpvzfXujf0O4wtaQHtDCLhlivwlTJ885Ld+eOgcz7r3TGQzeU+cSRoNHtD8tsJgzxVdYojFssw== dependencies: - "@next/env" "13.2.3" + "@next/env" "13.2.4" "@swc/helpers" "0.4.14" caniuse-lite "^1.0.30001406" postcss "8.4.14" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-android-arm-eabi" "13.2.3" - "@next/swc-android-arm64" "13.2.3" - "@next/swc-darwin-arm64" "13.2.3" - "@next/swc-darwin-x64" "13.2.3" - "@next/swc-freebsd-x64" "13.2.3" - "@next/swc-linux-arm-gnueabihf" "13.2.3" - "@next/swc-linux-arm64-gnu" "13.2.3" - "@next/swc-linux-arm64-musl" "13.2.3" - "@next/swc-linux-x64-gnu" "13.2.3" - "@next/swc-linux-x64-musl" "13.2.3" - "@next/swc-win32-arm64-msvc" "13.2.3" - "@next/swc-win32-ia32-msvc" "13.2.3" - "@next/swc-win32-x64-msvc" "13.2.3" + "@next/swc-android-arm-eabi" "13.2.4" + "@next/swc-android-arm64" "13.2.4" + "@next/swc-darwin-arm64" "13.2.4" + "@next/swc-darwin-x64" "13.2.4" + "@next/swc-freebsd-x64" "13.2.4" + "@next/swc-linux-arm-gnueabihf" "13.2.4" + "@next/swc-linux-arm64-gnu" "13.2.4" + "@next/swc-linux-arm64-musl" "13.2.4" + "@next/swc-linux-x64-gnu" "13.2.4" + "@next/swc-linux-x64-musl" "13.2.4" + "@next/swc-win32-arm64-msvc" "13.2.4" + "@next/swc-win32-ia32-msvc" "13.2.4" + "@next/swc-win32-x64-msvc" "13.2.4" picocolors@^1.0.0: version "1.0.0" diff --git a/packages/nextjs/src/server/wrapServerComponentWithSentry.ts b/packages/nextjs/src/server/wrapServerComponentWithSentry.ts index eac6a9405ac6..eab32207236c 100644 --- a/packages/nextjs/src/server/wrapServerComponentWithSentry.ts +++ b/packages/nextjs/src/server/wrapServerComponentWithSentry.ts @@ -54,18 +54,20 @@ export function wrapServerComponentWithSentry any> if (typeof maybePromiseResult === 'object' && maybePromiseResult !== null && 'then' in maybePromiseResult) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return maybePromiseResult.then( - (res: unknown) => { + Promise.resolve(maybePromiseResult).then( + () => { transaction.finish(); - return res; }, (e: Error) => { transaction.setStatus('internal_error'); captureException(e); transaction.finish(); - throw e; }, ); + + // It is very important that we return the original promise here, because Next.js attaches various properties + // to that promise and will throw if they are not on the returned value. + return maybePromiseResult; } else { transaction.finish(); return maybePromiseResult; From d6561b8028ba26f2e60d7acca06ef24d063919f4 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 14 Mar 2023 11:59:19 +0100 Subject: [PATCH 06/54] test(integration): Fix backgroundtab-pageload flaky test (#7442) --- .../tracing/browsertracing/backgroundtab-pageload/test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts b/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts index 8df4f0369041..f4ccf67b6c14 100644 --- a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts +++ b/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts @@ -8,12 +8,9 @@ sentryTest('should finish pageload transaction when the page goes background', a const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - - const pageloadTransactionPromise = getFirstSentryEnvelopeRequest(page); - await page.click('#go-background'); - const pageloadTransaction = await pageloadTransactionPromise; + const pageloadTransaction = await getFirstSentryEnvelopeRequest(page); expect(pageloadTransaction.contexts?.trace?.op).toBe('pageload'); expect(pageloadTransaction.contexts?.trace?.status).toBe('cancelled'); From 80b69390c4d0f2f1b9b760ab14add2a87522cb08 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Tue, 14 Mar 2023 12:29:51 +0000 Subject: [PATCH 07/54] feat(tracing): Add `@sentry-internal/tracing` package (#7445) --- package.json | 1 + packages/tracing-internal/.eslintrc.js | 11 ++++ packages/tracing-internal/LICENSE | 14 +++++ packages/tracing-internal/README.md | 11 ++++ packages/tracing-internal/jest.config.js | 1 + packages/tracing-internal/package.json | 52 +++++++++++++++++++ .../tracing-internal/rollup.npm.config.js | 3 ++ packages/tracing-internal/src/index.ts | 1 + packages/tracing-internal/tsconfig.json | 9 ++++ packages/tracing-internal/tsconfig.test.json | 12 +++++ packages/tracing-internal/tsconfig.types.json | 17 ++++++ 11 files changed, 132 insertions(+) create mode 100644 packages/tracing-internal/.eslintrc.js create mode 100644 packages/tracing-internal/LICENSE create mode 100644 packages/tracing-internal/README.md create mode 100644 packages/tracing-internal/jest.config.js create mode 100644 packages/tracing-internal/package.json create mode 100644 packages/tracing-internal/rollup.npm.config.js create mode 100644 packages/tracing-internal/src/index.ts create mode 100644 packages/tracing-internal/tsconfig.json create mode 100644 packages/tracing-internal/tsconfig.test.json create mode 100644 packages/tracing-internal/tsconfig.types.json diff --git a/package.json b/package.json index 3fb01dada5ed..cbba41b65df9 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "packages/svelte", "packages/sveltekit", "packages/tracing", + "packages/tracing-internal", "packages/types", "packages/typescript", "packages/utils", diff --git a/packages/tracing-internal/.eslintrc.js b/packages/tracing-internal/.eslintrc.js new file mode 100644 index 000000000000..efa4c594a069 --- /dev/null +++ b/packages/tracing-internal/.eslintrc.js @@ -0,0 +1,11 @@ +module.exports = { + extends: ['../../.eslintrc.js'], + overrides: [ + { + files: ['src/**'], + rules: { + '@sentry-internal/sdk/no-optional-chaining': 'off', + }, + }, + ], +}; diff --git a/packages/tracing-internal/LICENSE b/packages/tracing-internal/LICENSE new file mode 100644 index 000000000000..5113ccb2ac3d --- /dev/null +++ b/packages/tracing-internal/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2020 Sentry (https://sentry.io) and individual contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/tracing-internal/README.md b/packages/tracing-internal/README.md new file mode 100644 index 000000000000..89fb3d609619 --- /dev/null +++ b/packages/tracing-internal/README.md @@ -0,0 +1,11 @@ +

+ + Sentry + +

+ +## Sentry Internal Tracing Package - Do not use directly, for internal use only + +This is an internal package that is being used to migrate @sentry/tracing code to its respective runtime packages. + +For v8, @sentry/tracing will be dropped and the code in this package will be split into @sentry/browser and @sentry/node. diff --git a/packages/tracing-internal/jest.config.js b/packages/tracing-internal/jest.config.js new file mode 100644 index 000000000000..24f49ab59a4c --- /dev/null +++ b/packages/tracing-internal/jest.config.js @@ -0,0 +1 @@ +module.exports = require('../../jest/jest.config.js'); diff --git a/packages/tracing-internal/package.json b/packages/tracing-internal/package.json new file mode 100644 index 000000000000..2435a9eb20bf --- /dev/null +++ b/packages/tracing-internal/package.json @@ -0,0 +1,52 @@ +{ + "name": "@sentry-internal/tracing", + "version": "7.43.0", + "description": "Sentry Internal Tracing Package", + "repository": "git://github.com/getsentry/sentry-javascript.git", + "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/tracing-internal", + "author": "Sentry", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "main": "build/cjs/index.js", + "module": "build/esm/index.js", + "types": "build/types/index.d.ts", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@sentry/core": "7.43.0", + "@sentry/types": "7.43.0", + "@sentry/utils": "7.43.0", + "tslib": "^1.9.3" + }, + "devDependencies": { + "@sentry/browser": "7.43.0", + "@types/express": "^4.17.14" + }, + "scripts": { + "build": "run-p build:transpile build:types", + "build:dev": "yarn build", + "build:transpile": "rollup -c rollup.npm.config.js", + "build:types": "tsc -p tsconfig.types.json", + "build:watch": "run-p build:transpile:watch build:types:watch", + "build:dev:watch": "run-p build:transpile:watch build:types:watch", + "build:transpile:watch": "rollup -c rollup.npm.config.js --watch", + "build:types:watch": "tsc -p tsconfig.types.json --watch", + "build:tarball": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", + "clean": "rimraf build coverage sentry-tracing-*.tgz", + "fix": "run-s fix:eslint fix:prettier", + "fix:eslint": "eslint . --format stylish --fix", + "fix:prettier": "prettier --write \"{src,test,scripts}/**/**.ts\"", + "lint": "run-s lint:prettier lint:eslint", + "lint:eslint": "eslint . --format stylish", + "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", + "test:unit": "jest", + "test": "jest", + "test:watch": "jest --watch" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/packages/tracing-internal/rollup.npm.config.js b/packages/tracing-internal/rollup.npm.config.js new file mode 100644 index 000000000000..5a62b528ef44 --- /dev/null +++ b/packages/tracing-internal/rollup.npm.config.js @@ -0,0 +1,3 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js'; + +export default makeNPMConfigVariants(makeBaseNPMConfig()); diff --git a/packages/tracing-internal/src/index.ts b/packages/tracing-internal/src/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/packages/tracing-internal/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/tracing-internal/tsconfig.json b/packages/tracing-internal/tsconfig.json new file mode 100644 index 000000000000..bf45a09f2d71 --- /dev/null +++ b/packages/tracing-internal/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + + "include": ["src/**/*"], + + "compilerOptions": { + // package-specific options + } +} diff --git a/packages/tracing-internal/tsconfig.test.json b/packages/tracing-internal/tsconfig.test.json new file mode 100644 index 000000000000..87f6afa06b86 --- /dev/null +++ b/packages/tracing-internal/tsconfig.test.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + + "include": ["test/**/*"], + + "compilerOptions": { + // should include all types from `./tsconfig.json` plus types for all test frameworks used + "types": ["node", "jest"] + + // other package-specific, test-specific options + } +} diff --git a/packages/tracing-internal/tsconfig.types.json b/packages/tracing-internal/tsconfig.types.json new file mode 100644 index 000000000000..94190d6f0f73 --- /dev/null +++ b/packages/tracing-internal/tsconfig.types.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + // We don't need types for this because we don't ship it in our npm bundle. Skipping it here also lets us get around + // the fact that it introduces a dependency on `@sentry/browser` which doesn't exist anywhere else in the SDK, which + // then prevents us from building that and this at the same time when doing a parallellized build from the repo root + // level. + "exclude": [ + "src/index.bundle.ts", + "src/index.bundle.replay.ts" + ], + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "build/types" + } +} From 851037f88946dee1f7ed49035dcd14f5cad70e8e Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 14 Mar 2023 10:59:13 -0400 Subject: [PATCH 08/54] fix(profiling): catch sendProfile rejection (#7446) Co-authored-by: Abhijeet Prasad --- packages/browser/src/profiling/sendProfile.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/browser/src/profiling/sendProfile.ts b/packages/browser/src/profiling/sendProfile.ts index 6637e86c8f44..83ca990c516e 100644 --- a/packages/browser/src/profiling/sendProfile.ts +++ b/packages/browser/src/profiling/sendProfile.ts @@ -82,5 +82,8 @@ export function sendProfile(profileId: string, profile: ProcessedJSSelfProfile): logger.log('[Profiling] Envelope constructed, sending it'); } - void transport.send(envelope); + // Wrap in try/catch because send will throw in case of a network error. + transport.send(envelope).then(null, reason => { + __DEBUG_BUILD__ && logger.log('[Profiling] Error while sending event:', reason); + }); } From fb3d04670f943179cec018239232e374239b6920 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 14 Mar 2023 17:10:26 +0100 Subject: [PATCH 09/54] chore(tracing): Fix tarball generation path (#7460) --- packages/tracing-internal/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tracing-internal/package.json b/packages/tracing-internal/package.json index 2435a9eb20bf..a19bc69c6a80 100644 --- a/packages/tracing-internal/package.json +++ b/packages/tracing-internal/package.json @@ -34,7 +34,7 @@ "build:dev:watch": "run-p build:transpile:watch build:types:watch", "build:transpile:watch": "rollup -c rollup.npm.config.js --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", - "build:tarball": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm", + "build:tarball": "ts-node ../../scripts/prepack.ts && npm pack ./build", "clean": "rimraf build coverage sentry-tracing-*.tgz", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", From 2f886f613fc81a799c1d5556bc803f0d6df5045c Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Tue, 14 Mar 2023 16:18:30 +0000 Subject: [PATCH 10/54] feat(tracing): Move tracing code to internal package (#7451) --- packages/nextjs/test/integration/package.json | 1 + packages/remix/test/integration/package.json | 1 + .../src/browser/backgroundtab.ts | 0 .../src/browser/browsertracing.ts | 0 .../src/browser/index.ts | 0 .../src/browser/metrics/index.ts | 0 .../src/browser/metrics/utils.ts | 0 .../src/browser/request.ts | 0 .../src/browser/router.ts | 0 .../src/browser/types.ts | 0 .../src/browser/web-vitals/README.md | 0 .../src/browser/web-vitals/getCLS.ts | 0 .../src/browser/web-vitals/getFID.ts | 0 .../src/browser/web-vitals/getLCP.ts | 0 .../browser/web-vitals/lib/bindReporter.ts | 0 .../web-vitals/lib/generateUniqueID.ts | 0 .../web-vitals/lib/getActivationStart.ts | 0 .../web-vitals/lib/getNavigationEntry.ts | 0 .../web-vitals/lib/getVisibilityWatcher.ts | 0 .../src/browser/web-vitals/lib/initMetric.ts | 0 .../src/browser/web-vitals/lib/observe.ts | 0 .../src/browser/web-vitals/lib/onHidden.ts | 0 .../src/browser/web-vitals/types.ts | 0 .../src/browser/web-vitals/types/base.ts | 0 .../src/browser/web-vitals/types/cls.ts | 0 .../src/browser/web-vitals/types/fid.ts | 0 .../src/browser/web-vitals/types/lcp.ts | 0 .../src/browser/web-vitals/types/polyfills.ts | 0 .../src/errors.ts | 0 .../src/exports/index.ts | 0 .../src/extensions.ts | 0 packages/tracing-internal/src/index.ts | 35 ++++++++++++++++++- .../src/node/index.ts | 0 .../src/node/integrations/apollo.ts | 0 .../src/node/integrations/express.ts | 0 .../src/node/integrations/graphql.ts | 0 .../src/node/integrations/index.ts | 0 .../src/node/integrations/mongo.ts | 0 .../src/node/integrations/mysql.ts | 0 .../src/node/integrations/postgres.ts | 0 .../src/node/integrations/prisma.ts | 0 .../src/node/integrations/utils/node-utils.ts | 0 .../test/browser/backgroundtab.test.ts | 4 +-- .../test/browser/browsertracing.test.ts | 6 ++-- .../test/browser/metrics/index.test.ts | 0 .../test/browser/metrics/utils.test.ts | 0 .../test/browser/request.test.ts | 6 ++-- .../test/browser/router.test.ts | 0 .../test/errors.test.ts | 2 +- packages/tracing/package.json | 10 +++--- packages/tracing/src/index.bundle.base.ts | 4 +-- packages/tracing/src/index.ts | 32 ++--------------- packages/tracing/test/hub.test.ts | 3 +- .../test/integrations/apollo-nestjs.test.ts | 7 ++-- .../tracing/test/integrations/apollo.test.ts | 7 ++-- .../tracing/test/integrations/graphql.test.ts | 7 ++-- .../test/integrations/node/mongo.test.ts | 7 ++-- .../test/integrations/node/postgres.test.ts | 7 ++-- .../test/integrations/node/prisma.test.ts | 7 ++-- 59 files changed, 72 insertions(+), 74 deletions(-) rename packages/{tracing => tracing-internal}/src/browser/backgroundtab.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/browsertracing.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/index.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/metrics/index.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/metrics/utils.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/request.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/router.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/types.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/README.md (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/getCLS.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/getFID.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/getLCP.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/lib/bindReporter.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/lib/generateUniqueID.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/lib/getActivationStart.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/lib/getNavigationEntry.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/lib/getVisibilityWatcher.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/lib/initMetric.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/lib/observe.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/lib/onHidden.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/types.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/types/base.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/types/cls.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/types/fid.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/types/lcp.ts (100%) rename packages/{tracing => tracing-internal}/src/browser/web-vitals/types/polyfills.ts (100%) rename packages/{tracing => tracing-internal}/src/errors.ts (100%) rename packages/{tracing => tracing-internal}/src/exports/index.ts (100%) rename packages/{tracing => tracing-internal}/src/extensions.ts (100%) rename packages/{tracing => tracing-internal}/src/node/index.ts (100%) rename packages/{tracing => tracing-internal}/src/node/integrations/apollo.ts (100%) rename packages/{tracing => tracing-internal}/src/node/integrations/express.ts (100%) rename packages/{tracing => tracing-internal}/src/node/integrations/graphql.ts (100%) rename packages/{tracing => tracing-internal}/src/node/integrations/index.ts (100%) rename packages/{tracing => tracing-internal}/src/node/integrations/mongo.ts (100%) rename packages/{tracing => tracing-internal}/src/node/integrations/mysql.ts (100%) rename packages/{tracing => tracing-internal}/src/node/integrations/postgres.ts (100%) rename packages/{tracing => tracing-internal}/src/node/integrations/prisma.ts (100%) rename packages/{tracing => tracing-internal}/src/node/integrations/utils/node-utils.ts (100%) rename packages/{tracing => tracing-internal}/test/browser/backgroundtab.test.ts (93%) rename packages/{tracing => tracing-internal}/test/browser/browsertracing.test.ts (99%) rename packages/{tracing => tracing-internal}/test/browser/metrics/index.test.ts (100%) rename packages/{tracing => tracing-internal}/test/browser/metrics/utils.test.ts (100%) rename packages/{tracing => tracing-internal}/test/browser/request.test.ts (98%) rename packages/{tracing => tracing-internal}/test/browser/router.test.ts (100%) rename packages/{tracing => tracing-internal}/test/errors.test.ts (97%) diff --git a/packages/nextjs/test/integration/package.json b/packages/nextjs/test/integration/package.json index 4ff461b86f04..5c55363fe714 100644 --- a/packages/nextjs/test/integration/package.json +++ b/packages/nextjs/test/integration/package.json @@ -32,6 +32,7 @@ "@sentry/react": "file:../../../react", "@sentry/replay": "file:../../../replay", "@sentry/tracing": "file:../../../tracing", + "@sentry-internal/tracing": "file:../../../tracing-internal", "@sentry/types": "file:../../../types", "@sentry/utils": "file:../../../utils" } diff --git a/packages/remix/test/integration/package.json b/packages/remix/test/integration/package.json index 102a7844b1f9..3899f1acddad 100644 --- a/packages/remix/test/integration/package.json +++ b/packages/remix/test/integration/package.json @@ -30,6 +30,7 @@ "@sentry/react": "file:../../../react", "@sentry/replay": "file:../../../replay", "@sentry/tracing": "file:../../../tracing", + "@sentry-internal/tracing": "file:../../../tracing-internal", "@sentry/types": "file:../../../types", "@sentry/utils": "file:../../../utils" }, diff --git a/packages/tracing/src/browser/backgroundtab.ts b/packages/tracing-internal/src/browser/backgroundtab.ts similarity index 100% rename from packages/tracing/src/browser/backgroundtab.ts rename to packages/tracing-internal/src/browser/backgroundtab.ts diff --git a/packages/tracing/src/browser/browsertracing.ts b/packages/tracing-internal/src/browser/browsertracing.ts similarity index 100% rename from packages/tracing/src/browser/browsertracing.ts rename to packages/tracing-internal/src/browser/browsertracing.ts diff --git a/packages/tracing/src/browser/index.ts b/packages/tracing-internal/src/browser/index.ts similarity index 100% rename from packages/tracing/src/browser/index.ts rename to packages/tracing-internal/src/browser/index.ts diff --git a/packages/tracing/src/browser/metrics/index.ts b/packages/tracing-internal/src/browser/metrics/index.ts similarity index 100% rename from packages/tracing/src/browser/metrics/index.ts rename to packages/tracing-internal/src/browser/metrics/index.ts diff --git a/packages/tracing/src/browser/metrics/utils.ts b/packages/tracing-internal/src/browser/metrics/utils.ts similarity index 100% rename from packages/tracing/src/browser/metrics/utils.ts rename to packages/tracing-internal/src/browser/metrics/utils.ts diff --git a/packages/tracing/src/browser/request.ts b/packages/tracing-internal/src/browser/request.ts similarity index 100% rename from packages/tracing/src/browser/request.ts rename to packages/tracing-internal/src/browser/request.ts diff --git a/packages/tracing/src/browser/router.ts b/packages/tracing-internal/src/browser/router.ts similarity index 100% rename from packages/tracing/src/browser/router.ts rename to packages/tracing-internal/src/browser/router.ts diff --git a/packages/tracing/src/browser/types.ts b/packages/tracing-internal/src/browser/types.ts similarity index 100% rename from packages/tracing/src/browser/types.ts rename to packages/tracing-internal/src/browser/types.ts diff --git a/packages/tracing/src/browser/web-vitals/README.md b/packages/tracing-internal/src/browser/web-vitals/README.md similarity index 100% rename from packages/tracing/src/browser/web-vitals/README.md rename to packages/tracing-internal/src/browser/web-vitals/README.md diff --git a/packages/tracing/src/browser/web-vitals/getCLS.ts b/packages/tracing-internal/src/browser/web-vitals/getCLS.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/getCLS.ts rename to packages/tracing-internal/src/browser/web-vitals/getCLS.ts diff --git a/packages/tracing/src/browser/web-vitals/getFID.ts b/packages/tracing-internal/src/browser/web-vitals/getFID.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/getFID.ts rename to packages/tracing-internal/src/browser/web-vitals/getFID.ts diff --git a/packages/tracing/src/browser/web-vitals/getLCP.ts b/packages/tracing-internal/src/browser/web-vitals/getLCP.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/getLCP.ts rename to packages/tracing-internal/src/browser/web-vitals/getLCP.ts diff --git a/packages/tracing/src/browser/web-vitals/lib/bindReporter.ts b/packages/tracing-internal/src/browser/web-vitals/lib/bindReporter.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/lib/bindReporter.ts rename to packages/tracing-internal/src/browser/web-vitals/lib/bindReporter.ts diff --git a/packages/tracing/src/browser/web-vitals/lib/generateUniqueID.ts b/packages/tracing-internal/src/browser/web-vitals/lib/generateUniqueID.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/lib/generateUniqueID.ts rename to packages/tracing-internal/src/browser/web-vitals/lib/generateUniqueID.ts diff --git a/packages/tracing/src/browser/web-vitals/lib/getActivationStart.ts b/packages/tracing-internal/src/browser/web-vitals/lib/getActivationStart.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/lib/getActivationStart.ts rename to packages/tracing-internal/src/browser/web-vitals/lib/getActivationStart.ts diff --git a/packages/tracing/src/browser/web-vitals/lib/getNavigationEntry.ts b/packages/tracing-internal/src/browser/web-vitals/lib/getNavigationEntry.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/lib/getNavigationEntry.ts rename to packages/tracing-internal/src/browser/web-vitals/lib/getNavigationEntry.ts diff --git a/packages/tracing/src/browser/web-vitals/lib/getVisibilityWatcher.ts b/packages/tracing-internal/src/browser/web-vitals/lib/getVisibilityWatcher.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/lib/getVisibilityWatcher.ts rename to packages/tracing-internal/src/browser/web-vitals/lib/getVisibilityWatcher.ts diff --git a/packages/tracing/src/browser/web-vitals/lib/initMetric.ts b/packages/tracing-internal/src/browser/web-vitals/lib/initMetric.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/lib/initMetric.ts rename to packages/tracing-internal/src/browser/web-vitals/lib/initMetric.ts diff --git a/packages/tracing/src/browser/web-vitals/lib/observe.ts b/packages/tracing-internal/src/browser/web-vitals/lib/observe.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/lib/observe.ts rename to packages/tracing-internal/src/browser/web-vitals/lib/observe.ts diff --git a/packages/tracing/src/browser/web-vitals/lib/onHidden.ts b/packages/tracing-internal/src/browser/web-vitals/lib/onHidden.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/lib/onHidden.ts rename to packages/tracing-internal/src/browser/web-vitals/lib/onHidden.ts diff --git a/packages/tracing/src/browser/web-vitals/types.ts b/packages/tracing-internal/src/browser/web-vitals/types.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/types.ts rename to packages/tracing-internal/src/browser/web-vitals/types.ts diff --git a/packages/tracing/src/browser/web-vitals/types/base.ts b/packages/tracing-internal/src/browser/web-vitals/types/base.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/types/base.ts rename to packages/tracing-internal/src/browser/web-vitals/types/base.ts diff --git a/packages/tracing/src/browser/web-vitals/types/cls.ts b/packages/tracing-internal/src/browser/web-vitals/types/cls.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/types/cls.ts rename to packages/tracing-internal/src/browser/web-vitals/types/cls.ts diff --git a/packages/tracing/src/browser/web-vitals/types/fid.ts b/packages/tracing-internal/src/browser/web-vitals/types/fid.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/types/fid.ts rename to packages/tracing-internal/src/browser/web-vitals/types/fid.ts diff --git a/packages/tracing/src/browser/web-vitals/types/lcp.ts b/packages/tracing-internal/src/browser/web-vitals/types/lcp.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/types/lcp.ts rename to packages/tracing-internal/src/browser/web-vitals/types/lcp.ts diff --git a/packages/tracing/src/browser/web-vitals/types/polyfills.ts b/packages/tracing-internal/src/browser/web-vitals/types/polyfills.ts similarity index 100% rename from packages/tracing/src/browser/web-vitals/types/polyfills.ts rename to packages/tracing-internal/src/browser/web-vitals/types/polyfills.ts diff --git a/packages/tracing/src/errors.ts b/packages/tracing-internal/src/errors.ts similarity index 100% rename from packages/tracing/src/errors.ts rename to packages/tracing-internal/src/errors.ts diff --git a/packages/tracing/src/exports/index.ts b/packages/tracing-internal/src/exports/index.ts similarity index 100% rename from packages/tracing/src/exports/index.ts rename to packages/tracing-internal/src/exports/index.ts diff --git a/packages/tracing/src/extensions.ts b/packages/tracing-internal/src/extensions.ts similarity index 100% rename from packages/tracing/src/extensions.ts rename to packages/tracing-internal/src/extensions.ts diff --git a/packages/tracing-internal/src/index.ts b/packages/tracing-internal/src/index.ts index cb0ff5c3b541..1f0143253d55 100644 --- a/packages/tracing-internal/src/index.ts +++ b/packages/tracing-internal/src/index.ts @@ -1 +1,34 @@ -export {}; +export * from './exports'; + +import { addExtensionMethods } from './extensions'; +import * as Integrations from './node/integrations'; + +export { Integrations }; + +// BrowserTracing is already exported as part of `Integrations` above (and for the moment will remain so for +// backwards compatibility), but that interferes with treeshaking, so we also export it separately +// here. +// +// Previously we expected users to import tracing integrations like +// +// import { Integrations } from '@sentry/tracing'; +// const instance = new Integrations.BrowserTracing(); +// +// This makes the integrations unable to be treeshaken though. To address this, we now have +// this individual export. We now expect users to consume BrowserTracing like so: +// +// import { BrowserTracing } from '@sentry/tracing'; +// const instance = new BrowserTracing(); +// +// For an example of of the new usage of BrowserTracing, see @sentry/nextjs index.client.ts +export { BrowserTracing } from './browser'; + +export { + BROWSER_TRACING_INTEGRATION_ID, + instrumentOutgoingRequests, + defaultRequestInstrumentationOptions, +} from './browser'; + +export type { RequestInstrumentationOptions } from './browser'; + +export { addExtensionMethods }; diff --git a/packages/tracing/src/node/index.ts b/packages/tracing-internal/src/node/index.ts similarity index 100% rename from packages/tracing/src/node/index.ts rename to packages/tracing-internal/src/node/index.ts diff --git a/packages/tracing/src/node/integrations/apollo.ts b/packages/tracing-internal/src/node/integrations/apollo.ts similarity index 100% rename from packages/tracing/src/node/integrations/apollo.ts rename to packages/tracing-internal/src/node/integrations/apollo.ts diff --git a/packages/tracing/src/node/integrations/express.ts b/packages/tracing-internal/src/node/integrations/express.ts similarity index 100% rename from packages/tracing/src/node/integrations/express.ts rename to packages/tracing-internal/src/node/integrations/express.ts diff --git a/packages/tracing/src/node/integrations/graphql.ts b/packages/tracing-internal/src/node/integrations/graphql.ts similarity index 100% rename from packages/tracing/src/node/integrations/graphql.ts rename to packages/tracing-internal/src/node/integrations/graphql.ts diff --git a/packages/tracing/src/node/integrations/index.ts b/packages/tracing-internal/src/node/integrations/index.ts similarity index 100% rename from packages/tracing/src/node/integrations/index.ts rename to packages/tracing-internal/src/node/integrations/index.ts diff --git a/packages/tracing/src/node/integrations/mongo.ts b/packages/tracing-internal/src/node/integrations/mongo.ts similarity index 100% rename from packages/tracing/src/node/integrations/mongo.ts rename to packages/tracing-internal/src/node/integrations/mongo.ts diff --git a/packages/tracing/src/node/integrations/mysql.ts b/packages/tracing-internal/src/node/integrations/mysql.ts similarity index 100% rename from packages/tracing/src/node/integrations/mysql.ts rename to packages/tracing-internal/src/node/integrations/mysql.ts diff --git a/packages/tracing/src/node/integrations/postgres.ts b/packages/tracing-internal/src/node/integrations/postgres.ts similarity index 100% rename from packages/tracing/src/node/integrations/postgres.ts rename to packages/tracing-internal/src/node/integrations/postgres.ts diff --git a/packages/tracing/src/node/integrations/prisma.ts b/packages/tracing-internal/src/node/integrations/prisma.ts similarity index 100% rename from packages/tracing/src/node/integrations/prisma.ts rename to packages/tracing-internal/src/node/integrations/prisma.ts diff --git a/packages/tracing/src/node/integrations/utils/node-utils.ts b/packages/tracing-internal/src/node/integrations/utils/node-utils.ts similarity index 100% rename from packages/tracing/src/node/integrations/utils/node-utils.ts rename to packages/tracing-internal/src/node/integrations/utils/node-utils.ts diff --git a/packages/tracing/test/browser/backgroundtab.test.ts b/packages/tracing-internal/test/browser/backgroundtab.test.ts similarity index 93% rename from packages/tracing/test/browser/backgroundtab.test.ts rename to packages/tracing-internal/test/browser/backgroundtab.test.ts index ddbf76baa6c6..2c4ca381df3b 100644 --- a/packages/tracing/test/browser/backgroundtab.test.ts +++ b/packages/tracing-internal/test/browser/backgroundtab.test.ts @@ -2,9 +2,9 @@ import { BrowserClient } from '@sentry/browser'; import { Hub, makeMain } from '@sentry/core'; import { JSDOM } from 'jsdom'; -import { addExtensionMethods } from '../../src'; +import { addExtensionMethods } from '../../../tracing/src'; +import { getDefaultBrowserClientOptions } from '../../../tracing/test/testutils'; import { registerBackgroundTabDetection } from '../../src/browser/backgroundtab'; -import { getDefaultBrowserClientOptions } from '../testutils'; describe('registerBackgroundTabDetection', () => { let events: Record = {}; diff --git a/packages/tracing/test/browser/browsertracing.test.ts b/packages/tracing-internal/test/browser/browsertracing.test.ts similarity index 99% rename from packages/tracing/test/browser/browsertracing.test.ts rename to packages/tracing-internal/test/browser/browsertracing.test.ts index e2bee71db0a8..94abe88e6cbb 100644 --- a/packages/tracing/test/browser/browsertracing.test.ts +++ b/packages/tracing-internal/test/browser/browsertracing.test.ts @@ -5,13 +5,13 @@ import type { BaseTransportOptions, ClientOptions, DsnComponents } from '@sentry import type { InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; import { JSDOM } from 'jsdom'; -import type { IdleTransaction } from '../../src'; -import { getActiveTransaction } from '../../src'; +import type { IdleTransaction } from '../../../tracing/src'; +import { getActiveTransaction } from '../../../tracing/src'; +import { getDefaultBrowserClientOptions } from '../../../tracing/test/testutils'; import type { BrowserTracingOptions } from '../../src/browser/browsertracing'; import { BrowserTracing, getMetaContent } from '../../src/browser/browsertracing'; import { defaultRequestInstrumentationOptions } from '../../src/browser/request'; import { instrumentRoutingWithDefaults } from '../../src/browser/router'; -import { getDefaultBrowserClientOptions } from '../testutils'; let mockChangeHistory: ({ to, from }: { to: string; from?: string }) => void = () => undefined; diff --git a/packages/tracing/test/browser/metrics/index.test.ts b/packages/tracing-internal/test/browser/metrics/index.test.ts similarity index 100% rename from packages/tracing/test/browser/metrics/index.test.ts rename to packages/tracing-internal/test/browser/metrics/index.test.ts diff --git a/packages/tracing/test/browser/metrics/utils.test.ts b/packages/tracing-internal/test/browser/metrics/utils.test.ts similarity index 100% rename from packages/tracing/test/browser/metrics/utils.test.ts rename to packages/tracing-internal/test/browser/metrics/utils.test.ts diff --git a/packages/tracing/test/browser/request.test.ts b/packages/tracing-internal/test/browser/request.test.ts similarity index 98% rename from packages/tracing/test/browser/request.test.ts rename to packages/tracing-internal/test/browser/request.test.ts index 952362107878..4ca971a4947c 100644 --- a/packages/tracing/test/browser/request.test.ts +++ b/packages/tracing-internal/test/browser/request.test.ts @@ -2,11 +2,11 @@ import { BrowserClient } from '@sentry/browser'; import * as sentryCore from '@sentry/core'; import * as utils from '@sentry/utils'; -import type { Transaction } from '../../src'; -import { addExtensionMethods, Span, spanStatusfromHttpCode } from '../../src'; +import type { Transaction } from '../../../tracing/src'; +import { addExtensionMethods, Span, spanStatusfromHttpCode } from '../../../tracing/src'; +import { getDefaultBrowserClientOptions } from '../../../tracing/test/testutils'; import type { FetchData, XHRData } from '../../src/browser/request'; import { fetchCallback, instrumentOutgoingRequests, shouldAttachHeaders, xhrCallback } from '../../src/browser/request'; -import { getDefaultBrowserClientOptions } from '../testutils'; beforeAll(() => { addExtensionMethods(); diff --git a/packages/tracing/test/browser/router.test.ts b/packages/tracing-internal/test/browser/router.test.ts similarity index 100% rename from packages/tracing/test/browser/router.test.ts rename to packages/tracing-internal/test/browser/router.test.ts diff --git a/packages/tracing/test/errors.test.ts b/packages/tracing-internal/test/errors.test.ts similarity index 97% rename from packages/tracing/test/errors.test.ts rename to packages/tracing-internal/test/errors.test.ts index 70a2e4561671..1dba00b825b1 100644 --- a/packages/tracing/test/errors.test.ts +++ b/packages/tracing-internal/test/errors.test.ts @@ -2,8 +2,8 @@ import { BrowserClient } from '@sentry/browser'; import { addTracingExtensions, Hub, makeMain } from '@sentry/core'; import type { InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; +import { getDefaultBrowserClientOptions } from '../../tracing/test/testutils'; import { registerErrorInstrumentation } from '../src/errors'; -import { getDefaultBrowserClientOptions } from './testutils'; const mockAddInstrumentationHandler = jest.fn(); let mockErrorCallback: InstrumentHandlerCallback = () => undefined; diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 5ac7ac899983..7bae1b2c2e3b 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -16,14 +16,14 @@ "access": "public" }, "dependencies": { - "@sentry/core": "7.43.0", - "@sentry/types": "7.43.0", - "@sentry/utils": "7.43.0", - "tslib": "^1.9.3" + "@sentry-internal/tracing": "7.43.0" }, "devDependencies": { - "@sentry/browser": "7.43.0", "@sentry-internal/integration-shims": "7.43.0", + "@sentry/browser": "7.43.0", + "@sentry/core": "7.43.0", + "@sentry/types": "7.43.0", + "@sentry/utils": "7.43.0", "@types/express": "^4.17.14" }, "scripts": { diff --git a/packages/tracing/src/index.bundle.base.ts b/packages/tracing/src/index.bundle.base.ts index a753bab932d4..42e2c67fa4a8 100644 --- a/packages/tracing/src/index.bundle.base.ts +++ b/packages/tracing/src/index.bundle.base.ts @@ -53,13 +53,11 @@ export { } from '@sentry/browser'; export { SDK_VERSION } from '@sentry/browser'; +import { addExtensionMethods, BrowserTracing } from '@sentry-internal/tracing'; import { Integrations as BrowserIntegrations } from '@sentry/browser'; import type { Integration } from '@sentry/types'; import { GLOBAL_OBJ } from '@sentry/utils'; -import { BrowserTracing } from './browser'; -import { addExtensionMethods } from './extensions'; - export { Span } from '@sentry/core'; let windowIntegrations = {}; diff --git a/packages/tracing/src/index.ts b/packages/tracing/src/index.ts index b8456bc6b017..3f98af8cecd2 100644 --- a/packages/tracing/src/index.ts +++ b/packages/tracing/src/index.ts @@ -1,34 +1,6 @@ -export * from './exports'; +export * from '@sentry-internal/tracing'; -import { addExtensionMethods } from './extensions'; -import * as Integrations from './node/integrations'; - -export { Integrations }; - -// This is already exported as part of `Integrations` above (and for the moment will remain so for -// backwards compatibility), but that interferes with treeshaking, so we also export it separately -// here. -// -// Previously we expected users to import tracing integrations like -// -// import { Integrations } from '@sentry/tracing'; -// const instance = new Integrations.BrowserTracing(); -// -// This makes the integrations unable to be treeshaken though. To address this, we now have -// this individual export. We now expect users to consume BrowserTracing like so: -// -// import { BrowserTracing } from '@sentry/tracing'; -// const instance = new BrowserTracing(); -// -// For an example of of the new usage of BrowserTracing, see @sentry/nextjs index.client.ts -export { - BrowserTracing, - BROWSER_TRACING_INTEGRATION_ID, - instrumentOutgoingRequests, - defaultRequestInstrumentationOptions, -} from './browser'; - -export type { RequestInstrumentationOptions } from './browser'; +import { addExtensionMethods } from '@sentry-internal/tracing'; // Treeshakable guard to remove all code related to tracing declare const __SENTRY_TRACING__: boolean; diff --git a/packages/tracing/test/hub.test.ts b/packages/tracing/test/hub.test.ts index 85c0b7791374..789ffc8e939f 100644 --- a/packages/tracing/test/hub.test.ts +++ b/packages/tracing/test/hub.test.ts @@ -4,8 +4,7 @@ import { Hub, makeMain } from '@sentry/core'; import * as utilsModule from '@sentry/utils'; // for mocking import { logger } from '@sentry/utils'; -import { addExtensionMethods, extractTraceparentData, TRACEPARENT_REGEXP, Transaction } from '../src'; -import { BrowserTracing } from '../src/browser/browsertracing'; +import { addExtensionMethods, BrowserTracing, extractTraceparentData, TRACEPARENT_REGEXP, Transaction } from '../src'; import { addDOMPropertiesToGlobal, getDefaultBrowserClientOptions, diff --git a/packages/tracing/test/integrations/apollo-nestjs.test.ts b/packages/tracing/test/integrations/apollo-nestjs.test.ts index 981693875d7a..0a5116277cb1 100644 --- a/packages/tracing/test/integrations/apollo-nestjs.test.ts +++ b/packages/tracing/test/integrations/apollo-nestjs.test.ts @@ -2,8 +2,7 @@ import { Hub, Scope } from '@sentry/core'; import { logger } from '@sentry/utils'; -import { Span } from '../../src'; -import { Apollo } from '../../src/node/integrations/apollo'; +import { Integrations, Span } from '../../src'; import { getTestClient } from '../testutils'; type ApolloResolverGroup = { @@ -67,7 +66,7 @@ describe('setupOnce', () => { let GraphQLFactoryInstance: GraphQLFactory; beforeAll(() => { - new Apollo({ + new Integrations.Apollo({ useNestjs: true, }).setupOnce( () => undefined, @@ -113,7 +112,7 @@ describe('setupOnce', () => { const client = getTestClient({ instrumenter: 'otel' }); const hub = new Hub(client); - const integration = new Apollo({ useNestjs: true }); + const integration = new Integrations.Apollo({ useNestjs: true }); integration.setupOnce( () => {}, () => hub, diff --git a/packages/tracing/test/integrations/apollo.test.ts b/packages/tracing/test/integrations/apollo.test.ts index 910c866505e8..456c824f23ce 100644 --- a/packages/tracing/test/integrations/apollo.test.ts +++ b/packages/tracing/test/integrations/apollo.test.ts @@ -2,8 +2,7 @@ import { Hub, Scope } from '@sentry/core'; import { logger } from '@sentry/utils'; -import { Span } from '../../src'; -import { Apollo } from '../../src/node/integrations/apollo'; +import { Integrations, Span } from '../../src'; import { getTestClient } from '../testutils'; type ApolloResolverGroup = { @@ -69,7 +68,7 @@ describe('setupOnce', () => { let ApolloServer: ApolloServerBase; beforeAll(() => { - new Apollo().setupOnce( + new Integrations.Apollo().setupOnce( () => undefined, () => new Hub(undefined, scope), ); @@ -113,7 +112,7 @@ describe('setupOnce', () => { const client = getTestClient({ instrumenter: 'otel' }); const hub = new Hub(client); - const integration = new Apollo(); + const integration = new Integrations.Apollo(); integration.setupOnce( () => {}, () => hub, diff --git a/packages/tracing/test/integrations/graphql.test.ts b/packages/tracing/test/integrations/graphql.test.ts index 5d61a4acd5cb..ccd4348784bd 100644 --- a/packages/tracing/test/integrations/graphql.test.ts +++ b/packages/tracing/test/integrations/graphql.test.ts @@ -2,8 +2,7 @@ import { Hub, Scope } from '@sentry/core'; import { logger } from '@sentry/utils'; -import { Span } from '../../src'; -import { GraphQL } from '../../src/node/integrations/graphql'; +import { Integrations, Span } from '../../src'; import { getTestClient } from '../testutils'; const GQLExecute = { @@ -33,7 +32,7 @@ describe('setupOnce', () => { let childSpan: Span; beforeAll(() => { - new GraphQL().setupOnce( + new Integrations.GraphQL().setupOnce( () => undefined, () => new Hub(undefined, scope), ); @@ -66,7 +65,7 @@ describe('setupOnce', () => { const client = getTestClient({ instrumenter: 'otel' }); const hub = new Hub(client); - const integration = new GraphQL(); + const integration = new Integrations.GraphQL(); integration.setupOnce( () => {}, () => hub, diff --git a/packages/tracing/test/integrations/node/mongo.test.ts b/packages/tracing/test/integrations/node/mongo.test.ts index 5c3dd11255c4..70ac88e13109 100644 --- a/packages/tracing/test/integrations/node/mongo.test.ts +++ b/packages/tracing/test/integrations/node/mongo.test.ts @@ -2,8 +2,7 @@ import { Hub, Scope } from '@sentry/core'; import { logger } from '@sentry/utils'; -import { Span } from '../../../src'; -import { Mongo } from '../../../src/node/integrations/mongo'; +import { Integrations, Span } from '../../../src'; import { getTestClient } from '../../testutils'; class Collection { @@ -52,7 +51,7 @@ describe('patchOperation()', () => { let childSpan: Span; beforeAll(() => { - new Mongo({ + new Integrations.Mongo({ operations: ['insertOne', 'initializeOrderedBulkOp'], }).setupOnce( () => undefined, @@ -124,7 +123,7 @@ describe('patchOperation()', () => { const client = getTestClient({ instrumenter: 'otel' }); const hub = new Hub(client); - const integration = new Mongo(); + const integration = new Integrations.Mongo(); integration.setupOnce( () => {}, () => hub, diff --git a/packages/tracing/test/integrations/node/postgres.test.ts b/packages/tracing/test/integrations/node/postgres.test.ts index 08aa64cf368d..c2d5868596a1 100644 --- a/packages/tracing/test/integrations/node/postgres.test.ts +++ b/packages/tracing/test/integrations/node/postgres.test.ts @@ -2,8 +2,7 @@ import { Hub, Scope } from '@sentry/core'; import { logger } from '@sentry/utils'; -import { Span } from '../../../src'; -import { Postgres } from '../../../src/node/integrations/postgres'; +import { Integrations, Span } from '../../../src'; import { getTestClient } from '../../testutils'; class PgClient { @@ -51,7 +50,7 @@ describe('setupOnce', () => { let childSpan: Span; beforeAll(() => { - (pgApi === 'pg' ? new Postgres() : new Postgres({ usePgNative: true })).setupOnce( + (pgApi === 'pg' ? new Integrations.Postgres() : new Integrations.Postgres({ usePgNative: true })).setupOnce( () => undefined, () => new Hub(undefined, scope), ); @@ -107,7 +106,7 @@ describe('setupOnce', () => { const client = getTestClient({ instrumenter: 'otel' }); const hub = new Hub(client); - const integration = new Postgres(); + const integration = new Integrations.Postgres(); integration.setupOnce( () => {}, () => hub, diff --git a/packages/tracing/test/integrations/node/prisma.test.ts b/packages/tracing/test/integrations/node/prisma.test.ts index 95892f9c1686..735167161402 100644 --- a/packages/tracing/test/integrations/node/prisma.test.ts +++ b/packages/tracing/test/integrations/node/prisma.test.ts @@ -2,8 +2,7 @@ import { Hub, Scope } from '@sentry/core'; import { logger } from '@sentry/utils'; -import { Span } from '../../../src'; -import { Prisma } from '../../../src/node/integrations/prisma'; +import { Integrations, Span } from '../../../src'; import { getTestClient } from '../../testutils'; type PrismaMiddleware = (params: unknown, next: (params?: unknown) => Promise) => Promise; @@ -32,7 +31,7 @@ describe('setupOnce', function () { let childSpan: Span; beforeAll(() => { - new Prisma({ client: Client }).setupOnce( + new Integrations.Prisma({ client: Client }).setupOnce( () => undefined, () => new Hub(undefined, scope), ); @@ -66,7 +65,7 @@ describe('setupOnce', function () { const client = getTestClient({ instrumenter: 'otel' }); const hub = new Hub(client); - const integration = new Prisma({ client: Client }); + const integration = new Integrations.Prisma({ client: Client }); integration.setupOnce( () => {}, () => hub, From 2f3c93c5a504ddfc44f011bf498f2d192691af2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Mar 2023 16:11:11 +0000 Subject: [PATCH 11/54] chore(deps-dev): bump webpack in /scenarios/browser Bumps [webpack](https://github.com/webpack/webpack) from 5.62.1 to 5.76.0. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.62.1...v5.76.0) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- scenarios/browser/package.json | 2 +- scenarios/browser/yarn.lock | 88 +++++++++++++++++----------------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/scenarios/browser/package.json b/scenarios/browser/package.json index c622b30f7c9c..305641c25f9e 100644 --- a/scenarios/browser/package.json +++ b/scenarios/browser/package.json @@ -5,7 +5,7 @@ "version": "0.0.0", "devDependencies": { "html-webpack-plugin": "^5.5.0", - "webpack": "^5.62.1", + "webpack": "^5.76.0", "webpack-bundle-analyzer": "^4.5.0", "inquirer": "^8.2.0" } diff --git a/scenarios/browser/yarn.lock b/scenarios/browser/yarn.lock index 61d16f1c4d74..05a2d184cab2 100644 --- a/scenarios/browser/yarn.lock +++ b/scenarios/browser/yarn.lock @@ -47,10 +47,10 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@types/eslint-scope@^3.7.0": - version "3.7.1" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.1.tgz#8dc390a7b4f9dd9f1284629efce982e41612116e" - integrity sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g== +"@types/eslint-scope@^3.7.3": + version "3.7.4" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" + integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== dependencies: "@types/eslint" "*" "@types/estree" "*" @@ -63,10 +63,10 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^0.0.50": - version "0.0.50" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" - integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== +"@types/estree@*", "@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== "@types/html-minifier-terser@^6.0.0": version "6.0.0" @@ -224,10 +224,10 @@ acorn-walk@^8.0.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^8.0.4, acorn@^8.4.1, acorn@^8.5.0: - version "8.7.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== +acorn@^8.0.4, acorn@^8.5.0, acorn@^8.7.1: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== ajv-keywords@^3.5.2: version "3.5.2" @@ -476,10 +476,10 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -enhanced-resolve@^5.8.3: - version "5.8.3" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0" - integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA== +enhanced-resolve@^5.10.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" + integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -565,10 +565,10 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -graceful-fs@^4.1.2, graceful-fs@^4.2.4: - version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== +graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== gzip-size@^6.0.0: version "6.0.0" @@ -682,10 +682,10 @@ jest-worker@^27.0.6: merge-stream "^2.0.0" supports-color "^8.0.0" -json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" @@ -1052,10 +1052,10 @@ utila@~0.4: resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= -watchpack@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce" - integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA== +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -1082,40 +1082,40 @@ webpack-bundle-analyzer@^4.5.0: sirv "^1.0.7" ws "^7.3.1" -webpack-sources@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.1.tgz#251a7d9720d75ada1469ca07dbb62f3641a05b6d" - integrity sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA== +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.62.1: - version "5.62.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.62.1.tgz#06f09b56a7b1bb13ed5137ad4b118358a90c9505" - integrity sha512-jNLtnWChS2CMZ7vqWtztv0G6fYB5hz11Zsadp5tE7e4/66zVDj7/KUeQZOsOl8Hz5KrLJH1h2eIDl6AnlyE12Q== +webpack@^5.76.0: + version "5.76.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.0.tgz#f9fb9fb8c4a7dbdcd0d56a98e56b8a942ee2692c" + integrity sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA== dependencies: - "@types/eslint-scope" "^3.7.0" - "@types/estree" "^0.0.50" + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" "@webassemblyjs/ast" "1.11.1" "@webassemblyjs/wasm-edit" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1" - acorn "^8.4.1" + acorn "^8.7.1" acorn-import-assertions "^1.7.6" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.8.3" + enhanced-resolve "^5.10.0" es-module-lexer "^0.9.0" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.4" - json-parse-better-errors "^1.0.2" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" schema-utils "^3.1.0" tapable "^2.1.1" terser-webpack-plugin "^5.1.3" - watchpack "^2.2.0" - webpack-sources "^3.2.0" + watchpack "^2.4.0" + webpack-sources "^3.2.3" ws@^7.3.1: version "7.5.5" From a34dab631427c17af7a6f6eb411c9bbf5001c2a1 Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 14 Mar 2023 12:22:30 -0400 Subject: [PATCH 12/54] ref(utils): use test instead of indexof in stacktrace (#7417) --- packages/utils/src/stacktrace.ts | 37 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/packages/utils/src/stacktrace.ts b/packages/utils/src/stacktrace.ts index a5975f7a23b3..535e6b12108b 100644 --- a/packages/utils/src/stacktrace.ts +++ b/packages/utils/src/stacktrace.ts @@ -57,37 +57,38 @@ export function stackParserFromStackParserOptions(stackParser: StackParser | Sta } /** + * 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 */ -export function stripSentryFramesAndReverse(stack: StackFrame[]): StackFrame[] { +export function stripSentryFramesAndReverse(stack: ReadonlyArray): StackFrame[] { if (!stack.length) { return []; } - let localStack = stack; - - const firstFrameFunction = localStack[0].function || ''; - const lastFrameFunction = localStack[localStack.length - 1].function || ''; + const localStack = stack.slice(0, STACKTRACE_LIMIT); + const lastFrameFunction = localStack[localStack.length - 1].function; // If stack starts with one of our API calls, remove it (starts, meaning it's the top of the stack - aka last call) - if (firstFrameFunction.indexOf('captureMessage') !== -1 || firstFrameFunction.indexOf('captureException') !== -1) { - localStack = localStack.slice(1); + if (lastFrameFunction && /sentryWrapped/.test(lastFrameFunction)) { + localStack.pop(); } + // Reversing in the middle of the procedure allows us to just pop the values off the stack + localStack.reverse(); + + const firstFrameFunction = localStack[localStack.length - 1].function; // 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 (lastFrameFunction.indexOf('sentryWrapped') !== -1) { - localStack = localStack.slice(0, -1); + if (firstFrameFunction && /captureMessage|captureException/.test(firstFrameFunction)) { + localStack.pop(); } - // The frame where the crash happened, should be the last entry in the array - return localStack - .slice(0, STACKTRACE_LIMIT) - .map(frame => ({ - ...frame, - filename: frame.filename || localStack[0].filename, - function: frame.function || '?', - })) - .reverse(); + return localStack.map(frame => ({ + ...frame, + filename: frame.filename || localStack[localStack.length - 1].filename, + function: frame.function || '?', + })); } const defaultFunctionName = ''; From 42e542ed623194fcdc71e8bd2a464406f897c617 Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 14 Mar 2023 13:34:04 -0400 Subject: [PATCH 13/54] feat(core): emit `beforeEnvelope` hook (#7448) Co-authored-by: Abhijeet Prasad --- packages/core/src/transports/base.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 4a3be77e8531..19a92ebc8599 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -24,6 +24,8 @@ import { updateRateLimits, } from '@sentry/utils'; +import { getCurrentHub } from '../hub'; + export const DEFAULT_TRANSPORT_BUFFER_SIZE = 30; /** @@ -40,11 +42,14 @@ export function createTransport( ), ): Transport { let rateLimits: RateLimits = {}; - const flush = (timeout?: number): PromiseLike => buffer.drain(timeout); + const client = getCurrentHub().getClient(); function send(envelope: Envelope): PromiseLike { const filteredEnvelopeItems: EnvelopeItem[] = []; + if (client && client.emit) { + client.emit('beforeEnvelope', envelope); + } // Drop rate limited items from envelope forEachEnvelopeItem(envelope, (item, type) => { From b1ef00d1cea7dd0e98190e405777788521129020 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 14 Mar 2023 18:41:17 +0100 Subject: [PATCH 14/54] feat(sveltekit): Add wrapper for server load function (#7416) --- packages/sveltekit/.eslintrc.js | 9 ++ packages/sveltekit/src/server/index.ts | 1 + packages/sveltekit/src/server/load.ts | 64 +++++++++++ .../sveltekit/test/client/handleError.test.ts | 4 - .../sveltekit/test/server/handleError.test.ts | 6 +- packages/sveltekit/test/server/load.test.ts | 108 ++++++++++++++++++ 6 files changed, 183 insertions(+), 9 deletions(-) create mode 100644 packages/sveltekit/src/server/load.ts create mode 100644 packages/sveltekit/test/server/load.test.ts diff --git a/packages/sveltekit/.eslintrc.js b/packages/sveltekit/.eslintrc.js index 2d614f46733b..8fcc9ae58f9a 100644 --- a/packages/sveltekit/.eslintrc.js +++ b/packages/sveltekit/.eslintrc.js @@ -3,5 +3,14 @@ module.exports = { browser: true, node: true, }, + overrides: [ + { + files: ['*.ts'], + rules: { + // Turning this off because it's not working with @sveltejs/kit + 'import/no-unresolved': 'off', + }, + }, + ], extends: ['../../.eslintrc.js'], }; diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index 1e3aed034f1e..c7784d870c56 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -2,3 +2,4 @@ export * from '@sentry/node'; export { init } from './sdk'; export { handleErrorWithSentry } from './handleError'; +export { wrapLoadWithSentry } from './load'; diff --git a/packages/sveltekit/src/server/load.ts b/packages/sveltekit/src/server/load.ts new file mode 100644 index 000000000000..ef0433091a9e --- /dev/null +++ b/packages/sveltekit/src/server/load.ts @@ -0,0 +1,64 @@ +import { captureException } from '@sentry/node'; +import { addExceptionMechanism, isThenable, objectify } from '@sentry/utils'; +import type { HttpError, ServerLoad } from '@sveltejs/kit'; + +function isHttpError(err: unknown): err is HttpError { + return typeof err === 'object' && err !== null && 'status' in err && 'body' in err; +} + +function sendErrorToSentry(e: unknown): unknown { + // In case we have a primitive, wrap it in the equivalent wrapper class (string -> String, etc.) so that we can + // store a seen flag on it. + const objectifiedErr = objectify(e); + + // The error() helper is commonly used to throw errors in load functions: https://kit.svelte.dev/docs/modules#sveltejs-kit-error + // If we detect a thrown error that is an instance of HttpError, we don't want to capture 4xx errors as they + // could be noisy. + if (isHttpError(objectifiedErr) && objectifiedErr.status < 500 && objectifiedErr.status >= 400) { + return objectifiedErr; + } + + captureException(objectifiedErr, scope => { + scope.addEventProcessor(event => { + addExceptionMechanism(event, { + type: 'sveltekit', + handled: false, + data: { + function: 'load', + }, + }); + return event; + }); + + return scope; + }); + + return objectifiedErr; +} + +/** + * Wrap load function with Sentry + * + * @param origLoad SvelteKit user defined load function + */ +export function wrapLoadWithSentry(origLoad: ServerLoad): ServerLoad { + return new Proxy(origLoad, { + apply: (wrappingTarget, thisArg, args: Parameters) => { + let maybePromiseResult; + + try { + maybePromiseResult = wrappingTarget.apply(thisArg, args); + } catch (e) { + throw sendErrorToSentry(e); + } + + if (isThenable(maybePromiseResult)) { + Promise.resolve(maybePromiseResult).then(null, e => { + sendErrorToSentry(e); + }); + } + + return maybePromiseResult; + }, + }); +} diff --git a/packages/sveltekit/test/client/handleError.test.ts b/packages/sveltekit/test/client/handleError.test.ts index 6260d3dc4827..170322555308 100644 --- a/packages/sveltekit/test/client/handleError.test.ts +++ b/packages/sveltekit/test/client/handleError.test.ts @@ -1,8 +1,4 @@ import { Scope } from '@sentry/svelte'; -// For now disable the import/no-unresolved rule, because we don't have a way to -// tell eslint that we are only importing types from the @sveltejs/kit package without -// adding a custom resolver, which will take too much time. -// eslint-disable-next-line import/no-unresolved import type { HandleClientError, NavigationEvent } from '@sveltejs/kit'; import { vi } from 'vitest'; diff --git a/packages/sveltekit/test/server/handleError.test.ts b/packages/sveltekit/test/server/handleError.test.ts index da73ca0eef5d..afe7f5cbe6df 100644 --- a/packages/sveltekit/test/server/handleError.test.ts +++ b/packages/sveltekit/test/server/handleError.test.ts @@ -1,8 +1,4 @@ import { Scope } from '@sentry/node'; -// For now disable the import/no-unresolved rule, because we don't have a way to -// tell eslint that we are only importing types from the @sveltejs/kit package without -// adding a custom resolver, which will take too much time. -// eslint-disable-next-line import/no-unresolved import type { HandleServerError, RequestEvent } from '@sveltejs/kit'; import { vi } from 'vitest'; @@ -12,7 +8,7 @@ const mockCaptureException = vi.fn(); let mockScope = new Scope(); vi.mock('@sentry/node', async () => { - const original = (await vi.importActual('@sentry/core')) as any; + const original = (await vi.importActual('@sentry/node')) as any; return { ...original, captureException: (err: unknown, cb: (arg0: unknown) => unknown) => { diff --git a/packages/sveltekit/test/server/load.test.ts b/packages/sveltekit/test/server/load.test.ts new file mode 100644 index 000000000000..ec2503b945c4 --- /dev/null +++ b/packages/sveltekit/test/server/load.test.ts @@ -0,0 +1,108 @@ +import { Scope } from '@sentry/node'; +import type { ServerLoad } from '@sveltejs/kit'; +import { error } from '@sveltejs/kit'; +import { vi } from 'vitest'; + +import { wrapLoadWithSentry } from '../../src/server/load'; + +const mockCaptureException = vi.fn(); +let mockScope = new Scope(); + +vi.mock('@sentry/node', async () => { + const original = (await vi.importActual('@sentry/node')) as any; + return { + ...original, + captureException: (err: unknown, cb: (arg0: unknown) => unknown) => { + cb(mockScope); + mockCaptureException(err, cb); + return original.captureException(err, cb); + }, + }; +}); + +const mockAddExceptionMechanism = vi.fn(); + +vi.mock('@sentry/utils', async () => { + const original = (await vi.importActual('@sentry/utils')) as any; + return { + ...original, + addExceptionMechanism: (...args: unknown[]) => mockAddExceptionMechanism(...args), + }; +}); + +function getById(_id?: string) { + throw new Error('error'); +} + +describe('wrapLoadWithSentry', () => { + beforeEach(() => { + mockCaptureException.mockClear(); + mockAddExceptionMechanism.mockClear(); + mockScope = new Scope(); + }); + + it('calls captureException', async () => { + async function load({ params }: Parameters[0]): Promise> { + return { + post: getById(params.id), + }; + } + + const wrappedLoad = wrapLoadWithSentry(load); + const res = wrappedLoad({ params: { id: '1' } } as any); + await expect(res).rejects.toThrow(); + + expect(mockCaptureException).toHaveBeenCalledTimes(1); + }); + + describe('with error() helper', () => { + it.each([ + // [statusCode, timesCalled] + [400, 0], + [401, 0], + [403, 0], + [404, 0], + [409, 0], + [429, 0], + [499, 0], + [500, 1], + [501, 1], + [503, 1], + [504, 1], + ])('error with status code %s calls captureException %s times', async (code, times) => { + async function load({ params }: Parameters[0]): Promise> { + throw error(code, params.id); + } + + const wrappedLoad = wrapLoadWithSentry(load); + const res = wrappedLoad({ params: { id: '1' } } as any); + await expect(res).rejects.toThrow(); + + expect(mockCaptureException).toHaveBeenCalledTimes(times); + }); + }); + + it('adds an exception mechanism', async () => { + const addEventProcessorSpy = vi.spyOn(mockScope, 'addEventProcessor').mockImplementationOnce(callback => { + void callback({}, { event_id: 'fake-event-id' }); + return mockScope; + }); + + async function load({ params }: Parameters[0]): Promise> { + return { + post: getById(params.id), + }; + } + + const wrappedLoad = wrapLoadWithSentry(load); + const res = wrappedLoad({ params: { id: '1' } } as any); + await expect(res).rejects.toThrow(); + + expect(addEventProcessorSpy).toBeCalledTimes(1); + expect(mockAddExceptionMechanism).toBeCalledTimes(1); + expect(mockAddExceptionMechanism).toBeCalledWith( + {}, + { handled: false, type: 'sveltekit', data: { function: 'load' } }, + ); + }); +}); From defcb520374fa149a7ea3cbf646407d098d84e35 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 14 Mar 2023 19:25:55 +0100 Subject: [PATCH 15/54] feat(sveltekit): Add wrapper for client load function (#7447) --- packages/sveltekit/src/client/index.ts | 4 +- packages/sveltekit/src/client/load.ts | 53 ++++++++++++++ packages/sveltekit/src/index.types.ts | 4 +- packages/sveltekit/test/client/load.test.ts | 80 +++++++++++++++++++++ 4 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 packages/sveltekit/src/client/load.ts create mode 100644 packages/sveltekit/test/client/load.test.ts diff --git a/packages/sveltekit/src/client/index.ts b/packages/sveltekit/src/client/index.ts index dc6ad3407264..f60a353d8b1d 100644 --- a/packages/sveltekit/src/client/index.ts +++ b/packages/sveltekit/src/client/index.ts @@ -2,6 +2,4 @@ export * from '@sentry/svelte'; export { init } from './sdk'; export { handleErrorWithSentry } from './handleError'; - -// Just here so that eslint is happy until we export more stuff here -export const PLACEHOLDER_CLIENT = 'PLACEHOLDER'; +export { wrapLoadWithSentry } from './load'; diff --git a/packages/sveltekit/src/client/load.ts b/packages/sveltekit/src/client/load.ts new file mode 100644 index 000000000000..fbaa5f98799f --- /dev/null +++ b/packages/sveltekit/src/client/load.ts @@ -0,0 +1,53 @@ +import { captureException } from '@sentry/svelte'; +import { addExceptionMechanism, isThenable, objectify } from '@sentry/utils'; +import type { ServerLoad } from '@sveltejs/kit'; + +function sendErrorToSentry(e: unknown): unknown { + // In case we have a primitive, wrap it in the equivalent wrapper class (string -> String, etc.) so that we can + // store a seen flag on it. + const objectifiedErr = objectify(e); + + captureException(objectifiedErr, scope => { + scope.addEventProcessor(event => { + addExceptionMechanism(event, { + type: 'sveltekit', + handled: false, + data: { + function: 'load', + }, + }); + return event; + }); + + return scope; + }); + + return objectifiedErr; +} + +/** + * Wrap load function with Sentry + * + * @param origLoad SvelteKit user defined load function + */ +export function wrapLoadWithSentry(origLoad: ServerLoad): ServerLoad { + return new Proxy(origLoad, { + apply: (wrappingTarget, thisArg, args: Parameters) => { + let maybePromiseResult; + + try { + maybePromiseResult = wrappingTarget.apply(thisArg, args); + } catch (e) { + throw sendErrorToSentry(e); + } + + if (isThenable(maybePromiseResult)) { + Promise.resolve(maybePromiseResult).then(null, e => { + sendErrorToSentry(e); + }); + } + + return maybePromiseResult; + }, + }); +} diff --git a/packages/sveltekit/src/index.types.ts b/packages/sveltekit/src/index.types.ts index 06ff806a8377..f332a526019f 100644 --- a/packages/sveltekit/src/index.types.ts +++ b/packages/sveltekit/src/index.types.ts @@ -9,7 +9,7 @@ export * from './server'; import type { Integration, Options, StackParser } from '@sentry/types'; // eslint-disable-next-line import/no-unresolved -import type { HandleClientError, HandleServerError } from '@sveltejs/kit'; +import type { HandleClientError, HandleServerError, ServerLoad } from '@sveltejs/kit'; import type * as clientSdk from './client'; import type * as serverSdk from './server'; @@ -21,6 +21,8 @@ export declare function handleErrorWithSentry; +export declare function wrapLoadWithSentry(origLoad: S): S; + // We export a merged Integrations object so that users can (at least typing-wise) use all integrations everywhere. export declare const Integrations: typeof clientSdk.Integrations & typeof serverSdk.Integrations; diff --git a/packages/sveltekit/test/client/load.test.ts b/packages/sveltekit/test/client/load.test.ts new file mode 100644 index 000000000000..7cbfd3593c03 --- /dev/null +++ b/packages/sveltekit/test/client/load.test.ts @@ -0,0 +1,80 @@ +import { Scope } from '@sentry/svelte'; +import type { ServerLoad } from '@sveltejs/kit'; +import { vi } from 'vitest'; + +import { wrapLoadWithSentry } from '../../src/client/load'; + +const mockCaptureException = vi.fn(); +let mockScope = new Scope(); + +vi.mock('@sentry/svelte', async () => { + const original = (await vi.importActual('@sentry/svelte')) as any; + return { + ...original, + captureException: (err: unknown, cb: (arg0: unknown) => unknown) => { + cb(mockScope); + mockCaptureException(err, cb); + return original.captureException(err, cb); + }, + }; +}); + +const mockAddExceptionMechanism = vi.fn(); + +vi.mock('@sentry/utils', async () => { + const original = (await vi.importActual('@sentry/utils')) as any; + return { + ...original, + addExceptionMechanism: (...args: unknown[]) => mockAddExceptionMechanism(...args), + }; +}); + +function getById(_id?: string) { + throw new Error('error'); +} + +describe('wrapLoadWithSentry', () => { + beforeEach(() => { + mockCaptureException.mockClear(); + mockAddExceptionMechanism.mockClear(); + mockScope = new Scope(); + }); + + it('calls captureException', async () => { + async function load({ params }: Parameters[0]): Promise> { + return { + post: getById(params.id), + }; + } + + const wrappedLoad = wrapLoadWithSentry(load); + const res = wrappedLoad({ params: { id: '1' } } as any); + await expect(res).rejects.toThrow(); + + expect(mockCaptureException).toHaveBeenCalledTimes(1); + }); + + it('adds an exception mechanism', async () => { + const addEventProcessorSpy = vi.spyOn(mockScope, 'addEventProcessor').mockImplementationOnce(callback => { + void callback({}, { event_id: 'fake-event-id' }); + return mockScope; + }); + + async function load({ params }: Parameters[0]): Promise> { + return { + post: getById(params.id), + }; + } + + const wrappedLoad = wrapLoadWithSentry(load); + const res = wrappedLoad({ params: { id: '1' } } as any); + await expect(res).rejects.toThrow(); + + expect(addEventProcessorSpy).toBeCalledTimes(1); + expect(mockAddExceptionMechanism).toBeCalledTimes(1); + expect(mockAddExceptionMechanism).toBeCalledWith( + {}, + { handled: false, type: 'sveltekit', data: { function: 'load' } }, + ); + }); +}); From ef6b3c7877d5fc8031c08bb28b0ffafaeb01f501 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Tue, 14 Mar 2023 20:40:58 +0100 Subject: [PATCH 16/54] chore: Rename `integration-tests` -> `browser-integration-tests` (#7455) --- .github/workflows/build.yml | 11 +++++++---- package.json | 2 +- .../.eslintrc.js | 0 .../.gitignore | 0 .../README.md | 0 .../package.json | 0 .../playwright.config.ts | 0 .../suites/integrations/httpclient/fetch/subject.js | 0 .../suites/integrations/httpclient/fetch/test.ts | 0 .../suites/integrations/httpclient/init.js | 0 .../suites/integrations/httpclient/xhr/subject.js | 0 .../suites/integrations/httpclient/xhr/test.ts | 0 .../public-api/addBreadcrumb/empty_obj/subject.js | 0 .../public-api/addBreadcrumb/empty_obj/test.ts | 0 .../suites/public-api/addBreadcrumb/init.js | 0 .../addBreadcrumb/multiple_breadcrumbs/subject.js | 0 .../addBreadcrumb/multiple_breadcrumbs/test.ts | 0 .../addBreadcrumb/simple_breadcrumb/subject.js | 0 .../addBreadcrumb/simple_breadcrumb/test.ts | 0 .../addBreadcrumb/undefined_arg/subject.js | 0 .../public-api/addBreadcrumb/undefined_arg/test.ts | 0 .../captureException/empty_obj/subject.js | 0 .../public-api/captureException/empty_obj/test.ts | 0 .../suites/public-api/captureException/init.js | 0 .../captureException/simple_error/subject.js | 0 .../captureException/simple_error/test.ts | 0 .../captureException/undefined_arg/subject.js | 0 .../captureException/undefined_arg/test.ts | 0 .../suites/public-api/captureMessage/init.js | 0 .../captureMessage/simple_message/subject.js | 0 .../captureMessage/simple_message/test.ts | 0 .../public-api/captureMessage/with_level/subject.js | 0 .../public-api/captureMessage/with_level/test.ts | 0 .../configureScope/clear_scope/subject.js | 0 .../public-api/configureScope/clear_scope/test.ts | 0 .../suites/public-api/configureScope/init.js | 0 .../configureScope/set_properties/subject.js | 0 .../configureScope/set_properties/test.ts | 0 .../suites/public-api/init/console/test.ts | 0 .../suites/public-api/init/init.js | 0 .../subject.js | 0 .../eventListener-instrumentation-behaviour/test.ts | 0 .../eventListener-this-preservation/subject.js | 0 .../eventListener-this-preservation/test.ts | 0 .../eventListener-wrapping/subject.js | 0 .../instrumentation/eventListener-wrapping/test.ts | 0 .../instrumentation/eventListener/subject.js | 0 .../instrumentation/eventListener/test.ts | 0 .../suites/public-api/instrumentation/init.js | 0 .../suites/public-api/setContext/init.js | 0 .../setContext/multiple_contexts/subject.js | 0 .../public-api/setContext/multiple_contexts/test.ts | 0 .../setContext/non_serializable_context/subject.js | 0 .../setContext/non_serializable_context/test.ts | 0 .../public-api/setContext/simple_context/subject.js | 0 .../public-api/setContext/simple_context/test.ts | 0 .../suites/public-api/setExtra/init.js | 0 .../public-api/setExtra/multiple_extras/subject.js | 0 .../public-api/setExtra/multiple_extras/test.ts | 0 .../setExtra/non_serializable_extra/subject.js | 0 .../setExtra/non_serializable_extra/test.ts | 0 .../public-api/setExtra/simple_extra/subject.js | 0 .../suites/public-api/setExtra/simple_extra/test.ts | 0 .../setExtras/consecutive_calls/subject.js | 0 .../public-api/setExtras/consecutive_calls/test.ts | 0 .../suites/public-api/setExtras/init.js | 0 .../public-api/setExtras/multiple_extras/subject.js | 0 .../public-api/setExtras/multiple_extras/test.ts | 0 .../suites/public-api/setTag/init.js | 0 .../setTag/with_non_primitives/subject.js | 0 .../public-api/setTag/with_non_primitives/test.ts | 0 .../public-api/setTag/with_primitives/subject.js | 0 .../public-api/setTag/with_primitives/test.ts | 0 .../suites/public-api/setTags/init.js | 0 .../setTags/with_non_primitives/subject.js | 0 .../public-api/setTags/with_non_primitives/test.ts | 0 .../public-api/setTags/with_primitives/subject.js | 0 .../public-api/setTags/with_primitives/test.ts | 0 .../suites/public-api/setUser/init.js | 0 .../suites/public-api/setUser/unset_user/subject.js | 0 .../suites/public-api/setUser/unset_user/test.ts | 0 .../public-api/setUser/update_user/subject.js | 0 .../suites/public-api/setUser/update_user/test.ts | 0 .../suites/public-api/showReportDialog/init.js | 0 .../showReportDialog/inject-script/subject.js | 0 .../showReportDialog/inject-script/test.ts | 0 .../startTransaction/basic_usage/subject.js | 0 .../public-api/startTransaction/basic_usage/test.ts | 0 .../startTransaction/circular_data/subject.js | 0 .../startTransaction/circular_data/test.ts | 0 .../suites/public-api/startTransaction/init.js | 0 .../startTransaction/setMeasurement/subject.js | 0 .../startTransaction/setMeasurement/test.ts | 0 .../suites/public-api/withScope/init.js | 0 .../public-api/withScope/nested_scopes/subject.js | 0 .../public-api/withScope/nested_scopes/test.ts | 0 .../suites/replay/captureReplay/template.html | 0 .../suites/replay/captureReplay/test.ts | 0 .../suites/replay/captureReplayViaBrowser/init.js | 0 .../replay/captureReplayViaBrowser/template.html | 0 .../suites/replay/captureReplayViaBrowser/test.ts | 0 .../suites/replay/compression/init.js | 0 .../suites/replay/compression/subject.js | 0 .../suites/replay/compression/template.html | 0 .../suites/replay/compression/test.ts | 0 .../suites/replay/customEvents/init.js | 0 .../suites/replay/customEvents/subject.js | 0 .../suites/replay/customEvents/template.html | 0 .../suites/replay/customEvents/test.ts | 0 .../suites/replay/errorResponse/template.html | 0 .../suites/replay/errorResponse/test.ts | 0 .../suites/replay/errors/droppedError/init.js | 0 .../suites/replay/errors/droppedError/test.ts | 0 .../suites/replay/errors/errorMode/test.ts | 0 .../suites/replay/errors/errorsInSession/init.js | 0 .../suites/replay/errors/errorsInSession/test.ts | 0 .../suites/replay/errors/init.js | 0 .../suites/replay/errors/subject.js | 0 .../suites/replay/errors/template.html | 0 .../suites/replay/flushing/init.js | 0 .../suites/replay/flushing/subject.js | 0 .../suites/replay/flushing/template.html | 0 .../suites/replay/flushing/test.ts | 0 .../suites/replay/init.js | 0 .../suites/replay/multiple-pages/init.js | 0 .../suites/replay/multiple-pages/page-0.html | 0 .../suites/replay/multiple-pages/subject.js | 0 .../suites/replay/multiple-pages/template.html | 0 .../suites/replay/multiple-pages/test.ts | 0 .../test.ts-snapshots/seg-0-snap-full | 0 .../test.ts-snapshots/seg-0-snap-full-chromium | 0 .../test.ts-snapshots/seg-1-snap-incremental | 0 .../seg-1-snap-incremental-chromium | 0 .../test.ts-snapshots/seg-2-snap-full | 0 .../test.ts-snapshots/seg-2-snap-full-chromium | 0 .../test.ts-snapshots/seg-3-snap-incremental | 0 .../seg-3-snap-incremental-chromium | 0 .../test.ts-snapshots/seg-4-snap-full | 0 .../test.ts-snapshots/seg-4-snap-full-chromium | 0 .../test.ts-snapshots/seg-5-snap-incremental | 0 .../seg-5-snap-incremental-chromium | 0 .../test.ts-snapshots/seg-6-snap-incremental | 0 .../seg-6-snap-incremental-chromium | 0 .../test.ts-snapshots/seg-7-snap-incremental | 0 .../seg-7-snap-incremental-chromium | 0 .../test.ts-snapshots/seg-8-snap-full | 0 .../test.ts-snapshots/seg-8-snap-full-chromium | 0 .../test.ts-snapshots/seg-9-snap-incremental | 0 .../seg-9-snap-incremental-chromium | 0 .../suites/replay/privacyBlock/init.js | 0 .../suites/replay/privacyBlock/template.html | 0 .../suites/replay/privacyBlock/test.ts | 0 .../test.ts-snapshots/privacy-chromium.json | 0 .../test.ts-snapshots/privacy-firefox.json | 0 .../test.ts-snapshots/privacy-webkit.json | 0 .../privacyBlock/test.ts-snapshots/privacy.json | 0 .../suites/replay/privacyDefault/init.js | 0 .../suites/replay/privacyDefault/template.html | 0 .../suites/replay/privacyDefault/test.ts | 0 .../test.ts-snapshots/privacy-chromium.json | 0 .../test.ts-snapshots/privacy-firefox.json | 0 .../test.ts-snapshots/privacy-webkit.json | 0 .../privacyDefault/test.ts-snapshots/privacy.json | 0 .../suites/replay/privacyInput/init.js | 0 .../suites/replay/privacyInput/template.html | 0 .../suites/replay/privacyInput/test.ts | 0 .../suites/replay/privacyInputMaskAll/init.js | 0 .../suites/replay/privacyInputMaskAll/template.html | 0 .../suites/replay/privacyInputMaskAll/test.ts | 0 .../suites/replay/replayShim/init.js | 0 .../suites/replay/replayShim/template.html | 0 .../suites/replay/replayShim/test.ts | 0 .../suites/replay/requests/init.js | 0 .../suites/replay/requests/subject.js | 0 .../suites/replay/requests/template.html | 0 .../suites/replay/requests/test.ts | 0 .../suites/replay/sampling/init.js | 0 .../suites/replay/sampling/template.html | 0 .../suites/replay/sampling/test.ts | 0 .../suites/replay/sessionExpiry/init.js | 0 .../suites/replay/sessionExpiry/template.html | 0 .../suites/replay/sessionExpiry/test.ts | 0 .../test.ts-snapshots/snapshot-0-chromium.json | 0 .../test.ts-snapshots/snapshot-0-firefox.json | 0 .../test.ts-snapshots/snapshot-0-webkit.json | 0 .../sessionExpiry/test.ts-snapshots/snapshot-0.json | 0 .../test.ts-snapshots/snapshot-2-chromium.json | 0 .../test.ts-snapshots/snapshot-2-firefox.json | 0 .../test.ts-snapshots/snapshot-2-webkit.json | 0 .../sessionExpiry/test.ts-snapshots/snapshot-2.json | 0 .../suites/replay/sessionInactive/init.js | 0 .../suites/replay/sessionInactive/template.html | 0 .../suites/replay/sessionInactive/test.ts | 0 .../test.ts-snapshots/snapshot-0-chromium.json | 0 .../test.ts-snapshots/snapshot-0-firefox.json | 0 .../test.ts-snapshots/snapshot-0-webkit.json | 0 .../test.ts-snapshots/snapshot-0.json | 0 .../test.ts-snapshots/snapshot-1-chromium.json | 0 .../test.ts-snapshots/snapshot-1-firefox.json | 0 .../test.ts-snapshots/snapshot-1-webkit.json | 0 .../test.ts-snapshots/snapshot-1.json | 0 .../suites/replay/sessionMaxAge/init.js | 0 .../suites/replay/sessionMaxAge/template.html | 0 .../suites/replay/sessionMaxAge/test.ts | 0 .../test.ts-snapshots/snapshot-0-chromium.json | 0 .../test.ts-snapshots/snapshot-0-firefox.json | 0 .../test.ts-snapshots/snapshot-0-webkit.json | 0 .../sessionMaxAge/test.ts-snapshots/snapshot-0.json | 0 .../test.ts-snapshots/snapshot-2-chromium.json | 0 .../test.ts-snapshots/snapshot-2-firefox.json | 0 .../test.ts-snapshots/snapshot-2-webkit.json | 0 .../sessionMaxAge/test.ts-snapshots/snapshot-2.json | 0 .../suites/replay/unicode/compressed/init.js | 0 .../suites/replay/unicode/compressed/test.ts | 0 .../unicode-compressed-chromium.json | 0 .../unicode-compressed-firefox.json | 0 .../unicode-compressed-webkit.json | 0 .../test.ts-snapshots/unicode-compressed.json | 0 .../suites/replay/unicode/subject.js | 0 .../suites/replay/unicode/template.html | 0 .../suites/replay/unicode/uncompressed/init.js | 0 .../suites/replay/unicode/uncompressed/test.ts | 0 .../unicode-uncompressed-chromium.json | 0 .../unicode-uncompressed-firefox.json | 0 .../unicode-uncompressed-webkit.json | 0 .../test.ts-snapshots/unicode-uncompressed.json | 0 .../suites/sessions/init.js | 0 .../suites/sessions/start-session/template.html | 0 .../suites/sessions/start-session/test.ts | 0 .../suites/sessions/update-session/subject.js | 0 .../suites/sessions/update-session/template.html | 0 .../suites/sessions/update-session/test.ts | 0 .../suites/stacktraces/init.js | 0 .../protocol_containing_fn_identifiers/subject.js | 0 .../protocol_containing_fn_identifiers/test.ts | 0 .../stacktraces/protocol_fn_identifiers/subject.js | 0 .../stacktraces/protocol_fn_identifiers/test.ts | 0 .../stacktraces/regular_fn_identifiers/subject.js | 0 .../stacktraces/regular_fn_identifiers/test.ts | 0 .../suites/stacktraces/template.html | 0 .../browsertracing/backgroundtab-custom/init.js | 0 .../browsertracing/backgroundtab-custom/subject.js | 0 .../backgroundtab-custom/template.html | 0 .../browsertracing/backgroundtab-custom/test.ts | 0 .../backgroundtab-pageload/subject.js | 0 .../backgroundtab-pageload/template.html | 0 .../browsertracing/backgroundtab-pageload/test.ts | 0 .../suites/tracing/browsertracing/init.js | 0 .../browsertracing/interactions/assets/script.js | 0 .../tracing/browsertracing/interactions/init.js | 0 .../browsertracing/interactions/template.html | 0 .../tracing/browsertracing/interactions/test.ts | 0 .../long-tasks-disabled/assets/script.js | 0 .../browsertracing/long-tasks-disabled/init.js | 0 .../long-tasks-disabled/template.html | 0 .../browsertracing/long-tasks-disabled/test.ts | 0 .../long-tasks-enabled/assets/script.js | 0 .../browsertracing/long-tasks-enabled/init.js | 0 .../browsertracing/long-tasks-enabled/template.html | 0 .../browsertracing/long-tasks-enabled/test.ts | 0 .../suites/tracing/browsertracing/meta/init.js | 0 .../tracing/browsertracing/meta/template.html | 0 .../suites/tracing/browsertracing/meta/test.ts | 0 .../tracing/browsertracing/navigation/test.ts | 0 .../suites/tracing/browsertracing/pageload/test.ts | 0 .../tracePropagationTargets/customTargets/init.js | 0 .../customTargets/subject.js | 0 .../tracePropagationTargets/customTargets/test.ts | 0 .../customTargetsAndOrigins/init.js | 0 .../customTargetsAndOrigins/subject.js | 0 .../customTargetsAndOrigins/test.ts | 0 .../customTracingOrigins/init.js | 0 .../customTracingOrigins/subject.js | 0 .../customTracingOrigins/test.ts | 0 .../defaultTargetsMatch/init.js | 0 .../defaultTargetsMatch/subject.js | 0 .../defaultTargetsMatch/test.ts | 0 .../defaultTargetsNoMatch/init.js | 0 .../defaultTargetsNoMatch/subject.js | 0 .../defaultTargetsNoMatch/test.ts | 0 .../envelope-header-transaction-name/init.js | 0 .../envelope-header-transaction-name/test.ts | 0 .../suites/tracing/envelope-header/init.js | 0 .../suites/tracing/envelope-header/test.ts | 0 .../tracing/metrics/connection-rtt/template.html | 0 .../suites/tracing/metrics/connection-rtt/test.ts | 0 .../suites/tracing/metrics/init.js | 0 .../tracing/metrics/pageload-browser-spans/test.ts | 0 .../pageload-resource-spans/assets/image.svg | 0 .../pageload-resource-spans/assets/script.js | 0 .../pageload-resource-spans/assets/style.css | 0 .../metrics/pageload-resource-spans/template.html | 0 .../tracing/metrics/pageload-resource-spans/test.ts | 0 .../tracing/metrics/web-vitals-cls/subject.js | 0 .../tracing/metrics/web-vitals-cls/template.html | 0 .../suites/tracing/metrics/web-vitals-cls/test.ts | 0 .../tracing/metrics/web-vitals-fid/template.html | 0 .../suites/tracing/metrics/web-vitals-fid/test.ts | 0 .../tracing/metrics/web-vitals-fp-fcp/template.html | 0 .../tracing/metrics/web-vitals-fp-fcp/test.ts | 0 .../web-vitals-lcp/assets/sentry-logo-600x179.png | Bin .../tracing/metrics/web-vitals-lcp/template.html | 0 .../suites/tracing/metrics/web-vitals-lcp/test.ts | 0 .../tracing/metrics/web-vitals-ttfb/template.html | 0 .../suites/tracing/metrics/web-vitals-ttfb/test.ts | 0 .../suites/tracing/request/fetch/subject.js | 0 .../suites/tracing/request/fetch/test.ts | 0 .../suites/tracing/request/init.js | 0 .../suites/tracing/request/xhr/subject.js | 0 .../suites/tracing/request/xhr/test.ts | 0 .../suites/transport/offline/init.js | 0 .../suites/transport/offline/queued/subject.js | 0 .../suites/transport/offline/queued/test.ts | 0 .../suites/wasm/init.js | 0 .../suites/wasm/simple.wasm | Bin .../suites/wasm/subject.js | 0 .../suites/wasm/test.ts | 0 .../tsconfig.json | 0 .../utils/defaults/template.html | 0 .../utils/fixtures.ts | 0 .../utils/generatePage.ts | 0 .../utils/generatePlugin.ts | 0 .../utils/helpers.ts | 0 .../utils/replayEventTemplates.ts | 0 .../utils/replayHelpers.ts | 0 .../utils/wasmHelpers.ts | 0 .../utils/web-vitals/cls.ts | 0 .../webpack.config.ts | 0 .../test/integration/test/client/utils/helpers.ts | 2 +- .../test/integration/test/client/utils/helpers.ts | 2 +- 330 files changed, 10 insertions(+), 7 deletions(-) rename packages/{integration-tests => browser-integration-tests}/.eslintrc.js (100%) rename packages/{integration-tests => browser-integration-tests}/.gitignore (100%) rename packages/{integration-tests => browser-integration-tests}/README.md (100%) rename packages/{integration-tests => browser-integration-tests}/package.json (100%) rename packages/{integration-tests => browser-integration-tests}/playwright.config.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/integrations/httpclient/fetch/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/integrations/httpclient/fetch/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/integrations/httpclient/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/integrations/httpclient/xhr/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/integrations/httpclient/xhr/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/addBreadcrumb/empty_obj/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/addBreadcrumb/empty_obj/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/addBreadcrumb/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/addBreadcrumb/multiple_breadcrumbs/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/addBreadcrumb/simple_breadcrumb/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/addBreadcrumb/undefined_arg/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/addBreadcrumb/undefined_arg/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/captureException/empty_obj/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/captureException/empty_obj/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/captureException/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/captureException/simple_error/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/captureException/simple_error/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/captureException/undefined_arg/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/captureException/undefined_arg/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/captureMessage/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/captureMessage/simple_message/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/captureMessage/simple_message/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/captureMessage/with_level/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/captureMessage/with_level/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/configureScope/clear_scope/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/configureScope/clear_scope/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/configureScope/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/configureScope/set_properties/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/configureScope/set_properties/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/init/console/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/init/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/instrumentation/eventListener-this-preservation/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/instrumentation/eventListener-this-preservation/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/instrumentation/eventListener-wrapping/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/instrumentation/eventListener-wrapping/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/instrumentation/eventListener/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/instrumentation/eventListener/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/instrumentation/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setContext/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setContext/multiple_contexts/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setContext/multiple_contexts/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setContext/non_serializable_context/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setContext/non_serializable_context/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setContext/simple_context/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setContext/simple_context/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setExtra/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setExtra/multiple_extras/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setExtra/multiple_extras/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setExtra/non_serializable_extra/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setExtra/non_serializable_extra/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setExtra/simple_extra/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setExtra/simple_extra/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setExtras/consecutive_calls/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setExtras/consecutive_calls/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setExtras/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setExtras/multiple_extras/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setExtras/multiple_extras/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setTag/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setTag/with_non_primitives/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setTag/with_non_primitives/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setTag/with_primitives/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setTag/with_primitives/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setTags/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setTags/with_non_primitives/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setTags/with_non_primitives/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setTags/with_primitives/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setTags/with_primitives/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setUser/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setUser/unset_user/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setUser/unset_user/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setUser/update_user/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/setUser/update_user/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/showReportDialog/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/showReportDialog/inject-script/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/showReportDialog/inject-script/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/startTransaction/basic_usage/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/startTransaction/basic_usage/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/startTransaction/circular_data/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/startTransaction/circular_data/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/startTransaction/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/startTransaction/setMeasurement/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/startTransaction/setMeasurement/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/withScope/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/withScope/nested_scopes/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/public-api/withScope/nested_scopes/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/captureReplay/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/captureReplay/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/captureReplayViaBrowser/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/captureReplayViaBrowser/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/captureReplayViaBrowser/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/compression/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/compression/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/compression/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/compression/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/customEvents/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/customEvents/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/customEvents/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/customEvents/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/errorResponse/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/errorResponse/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/errors/droppedError/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/errors/droppedError/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/errors/errorMode/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/errors/errorsInSession/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/errors/errorsInSession/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/errors/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/errors/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/errors/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/flushing/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/flushing/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/flushing/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/flushing/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/page-0.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full-chromium (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental-chromium (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full-chromium (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental-chromium (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full-chromium (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental-chromium (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental-chromium (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental-chromium (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full-chromium (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental-chromium (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyBlock/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyBlock/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyBlock/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyBlock/test.ts-snapshots/privacy-chromium.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyBlock/test.ts-snapshots/privacy-firefox.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyBlock/test.ts-snapshots/privacy-webkit.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyBlock/test.ts-snapshots/privacy.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyDefault/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyDefault/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyDefault/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyDefault/test.ts-snapshots/privacy-chromium.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyDefault/test.ts-snapshots/privacy-firefox.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyDefault/test.ts-snapshots/privacy-webkit.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyDefault/test.ts-snapshots/privacy.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyInput/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyInput/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyInput/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyInputMaskAll/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyInputMaskAll/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/privacyInputMaskAll/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/replayShim/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/replayShim/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/replayShim/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/requests/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/requests/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/requests/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/requests/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sampling/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sampling/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sampling/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionExpiry/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionExpiry/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionExpiry/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-chromium.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-firefox.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-webkit.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-chromium.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-firefox.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-webkit.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionInactive/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionInactive/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionInactive/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-chromium.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-firefox.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-webkit.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-chromium.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-firefox.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-webkit.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionMaxAge/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionMaxAge/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionMaxAge/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-chromium.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-firefox.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-webkit.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-chromium.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-firefox.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-webkit.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/compressed/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/compressed/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-chromium.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-firefox.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-webkit.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/uncompressed/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/uncompressed/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-chromium.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-firefox.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-webkit.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed.json (100%) rename packages/{integration-tests => browser-integration-tests}/suites/sessions/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/sessions/start-session/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/sessions/start-session/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/sessions/update-session/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/sessions/update-session/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/sessions/update-session/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/stacktraces/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/stacktraces/protocol_containing_fn_identifiers/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/stacktraces/protocol_containing_fn_identifiers/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/stacktraces/protocol_fn_identifiers/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/stacktraces/protocol_fn_identifiers/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/stacktraces/regular_fn_identifiers/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/stacktraces/regular_fn_identifiers/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/stacktraces/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/backgroundtab-custom/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/backgroundtab-custom/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/backgroundtab-custom/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/backgroundtab-custom/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/backgroundtab-pageload/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/backgroundtab-pageload/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/backgroundtab-pageload/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/interactions/assets/script.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/interactions/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/interactions/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/interactions/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/long-tasks-disabled/assets/script.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/long-tasks-disabled/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/long-tasks-disabled/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/long-tasks-disabled/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/long-tasks-enabled/assets/script.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/long-tasks-enabled/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/long-tasks-enabled/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/long-tasks-enabled/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/meta/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/meta/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/meta/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/navigation/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/pageload/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/customTargets/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/customTargets/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/customTargets/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/envelope-header-transaction-name/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/envelope-header-transaction-name/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/envelope-header/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/envelope-header/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/connection-rtt/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/connection-rtt/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/pageload-browser-spans/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/pageload-resource-spans/assets/image.svg (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/pageload-resource-spans/assets/script.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/pageload-resource-spans/assets/style.css (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/pageload-resource-spans/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/pageload-resource-spans/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/web-vitals-cls/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/web-vitals-cls/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/web-vitals-cls/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/web-vitals-fid/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/web-vitals-fid/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/web-vitals-fp-fcp/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/web-vitals-fp-fcp/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/web-vitals-lcp/assets/sentry-logo-600x179.png (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/web-vitals-lcp/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/web-vitals-lcp/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/web-vitals-ttfb/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/metrics/web-vitals-ttfb/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/request/fetch/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/request/fetch/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/request/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/request/xhr/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/tracing/request/xhr/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/transport/offline/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/transport/offline/queued/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/transport/offline/queued/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/suites/wasm/init.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/wasm/simple.wasm (100%) rename packages/{integration-tests => browser-integration-tests}/suites/wasm/subject.js (100%) rename packages/{integration-tests => browser-integration-tests}/suites/wasm/test.ts (100%) rename packages/{integration-tests => browser-integration-tests}/tsconfig.json (100%) rename packages/{integration-tests => browser-integration-tests}/utils/defaults/template.html (100%) rename packages/{integration-tests => browser-integration-tests}/utils/fixtures.ts (100%) rename packages/{integration-tests => browser-integration-tests}/utils/generatePage.ts (100%) rename packages/{integration-tests => browser-integration-tests}/utils/generatePlugin.ts (100%) rename packages/{integration-tests => browser-integration-tests}/utils/helpers.ts (100%) rename packages/{integration-tests => browser-integration-tests}/utils/replayEventTemplates.ts (100%) rename packages/{integration-tests => browser-integration-tests}/utils/replayHelpers.ts (100%) rename packages/{integration-tests => browser-integration-tests}/utils/wasmHelpers.ts (100%) rename packages/{integration-tests => browser-integration-tests}/utils/web-vitals/cls.ts (100%) rename packages/{integration-tests => browser-integration-tests}/webpack.config.ts (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 981133613300..f5d0364b7cda 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -99,7 +99,7 @@ jobs: browser_integration: - *shared - *browser - - 'packages/integration-tests/**' + - 'packages/browser-integration-tests/**' ember: - *shared - *browser @@ -144,7 +144,8 @@ jobs: has_gitflow_label: ${{ github.event_name == 'pull_request' && contains(steps.pr-labels.outputs.labels, ' Gitflow ') }} force_skip_cache: - ${{ github.event_name == 'schedule' || (github.event_name == 'pull_request' && contains(steps.pr-labels.outputs.labels, ' ci-skip-cache ')) }} + ${{ github.event_name == 'schedule' || (github.event_name == 'pull_request' && + contains(steps.pr-labels.outputs.labels, ' ci-skip-cache ')) }} job_install_deps: name: Install Dependencies @@ -274,7 +275,9 @@ jobs: needs: [job_get_metadata, job_build] timeout-minutes: 15 runs-on: ubuntu-20.04 - if: github.event_name == 'pull_request' || needs.job_get_metadata.outputs.is_develop == 'true' || needs.job_get_metadata.outputs.is_release == 'true' + if: + github.event_name == 'pull_request' || needs.job_get_metadata.outputs.is_develop == 'true' || + needs.job_get_metadata.outputs.is_release == 'true' steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v3 @@ -540,7 +543,7 @@ jobs: PW_BUNDLE: ${{ matrix.bundle }} PW_TRACING_ONLY: ${{ matrix.tracing_only }} run: | - cd packages/integration-tests + cd packages/browser-integration-tests yarn test:ci job_browser_integration_tests: diff --git a/package.json b/package.json index cbba41b65df9..3e720b7a5af7 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "packages/angular", "packages/angular-ivy", "packages/browser", + "packages/browser-integration-tests", "packages/core", "packages/e2e-tests", "packages/ember", @@ -45,7 +46,6 @@ "packages/eslint-plugin-sdk", "packages/gatsby", "packages/hub", - "packages/integration-tests", "packages/integrations", "packages/integration-shims", "packages/overhead-metrics", diff --git a/packages/integration-tests/.eslintrc.js b/packages/browser-integration-tests/.eslintrc.js similarity index 100% rename from packages/integration-tests/.eslintrc.js rename to packages/browser-integration-tests/.eslintrc.js diff --git a/packages/integration-tests/.gitignore b/packages/browser-integration-tests/.gitignore similarity index 100% rename from packages/integration-tests/.gitignore rename to packages/browser-integration-tests/.gitignore diff --git a/packages/integration-tests/README.md b/packages/browser-integration-tests/README.md similarity index 100% rename from packages/integration-tests/README.md rename to packages/browser-integration-tests/README.md diff --git a/packages/integration-tests/package.json b/packages/browser-integration-tests/package.json similarity index 100% rename from packages/integration-tests/package.json rename to packages/browser-integration-tests/package.json diff --git a/packages/integration-tests/playwright.config.ts b/packages/browser-integration-tests/playwright.config.ts similarity index 100% rename from packages/integration-tests/playwright.config.ts rename to packages/browser-integration-tests/playwright.config.ts diff --git a/packages/integration-tests/suites/integrations/httpclient/fetch/subject.js b/packages/browser-integration-tests/suites/integrations/httpclient/fetch/subject.js similarity index 100% rename from packages/integration-tests/suites/integrations/httpclient/fetch/subject.js rename to packages/browser-integration-tests/suites/integrations/httpclient/fetch/subject.js diff --git a/packages/integration-tests/suites/integrations/httpclient/fetch/test.ts b/packages/browser-integration-tests/suites/integrations/httpclient/fetch/test.ts similarity index 100% rename from packages/integration-tests/suites/integrations/httpclient/fetch/test.ts rename to packages/browser-integration-tests/suites/integrations/httpclient/fetch/test.ts diff --git a/packages/integration-tests/suites/integrations/httpclient/init.js b/packages/browser-integration-tests/suites/integrations/httpclient/init.js similarity index 100% rename from packages/integration-tests/suites/integrations/httpclient/init.js rename to packages/browser-integration-tests/suites/integrations/httpclient/init.js diff --git a/packages/integration-tests/suites/integrations/httpclient/xhr/subject.js b/packages/browser-integration-tests/suites/integrations/httpclient/xhr/subject.js similarity index 100% rename from packages/integration-tests/suites/integrations/httpclient/xhr/subject.js rename to packages/browser-integration-tests/suites/integrations/httpclient/xhr/subject.js diff --git a/packages/integration-tests/suites/integrations/httpclient/xhr/test.ts b/packages/browser-integration-tests/suites/integrations/httpclient/xhr/test.ts similarity index 100% rename from packages/integration-tests/suites/integrations/httpclient/xhr/test.ts rename to packages/browser-integration-tests/suites/integrations/httpclient/xhr/test.ts diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/empty_obj/subject.js b/packages/browser-integration-tests/suites/public-api/addBreadcrumb/empty_obj/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/addBreadcrumb/empty_obj/subject.js rename to packages/browser-integration-tests/suites/public-api/addBreadcrumb/empty_obj/subject.js diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/empty_obj/test.ts b/packages/browser-integration-tests/suites/public-api/addBreadcrumb/empty_obj/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/addBreadcrumb/empty_obj/test.ts rename to packages/browser-integration-tests/suites/public-api/addBreadcrumb/empty_obj/test.ts diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/init.js b/packages/browser-integration-tests/suites/public-api/addBreadcrumb/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/addBreadcrumb/init.js rename to packages/browser-integration-tests/suites/public-api/addBreadcrumb/init.js diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/subject.js b/packages/browser-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/subject.js rename to packages/browser-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/subject.js diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts b/packages/browser-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts rename to packages/browser-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/subject.js b/packages/browser-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/subject.js rename to packages/browser-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/subject.js diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts b/packages/browser-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts rename to packages/browser-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/undefined_arg/subject.js b/packages/browser-integration-tests/suites/public-api/addBreadcrumb/undefined_arg/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/addBreadcrumb/undefined_arg/subject.js rename to packages/browser-integration-tests/suites/public-api/addBreadcrumb/undefined_arg/subject.js diff --git a/packages/integration-tests/suites/public-api/addBreadcrumb/undefined_arg/test.ts b/packages/browser-integration-tests/suites/public-api/addBreadcrumb/undefined_arg/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/addBreadcrumb/undefined_arg/test.ts rename to packages/browser-integration-tests/suites/public-api/addBreadcrumb/undefined_arg/test.ts diff --git a/packages/integration-tests/suites/public-api/captureException/empty_obj/subject.js b/packages/browser-integration-tests/suites/public-api/captureException/empty_obj/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/captureException/empty_obj/subject.js rename to packages/browser-integration-tests/suites/public-api/captureException/empty_obj/subject.js diff --git a/packages/integration-tests/suites/public-api/captureException/empty_obj/test.ts b/packages/browser-integration-tests/suites/public-api/captureException/empty_obj/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/captureException/empty_obj/test.ts rename to packages/browser-integration-tests/suites/public-api/captureException/empty_obj/test.ts diff --git a/packages/integration-tests/suites/public-api/captureException/init.js b/packages/browser-integration-tests/suites/public-api/captureException/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/captureException/init.js rename to packages/browser-integration-tests/suites/public-api/captureException/init.js diff --git a/packages/integration-tests/suites/public-api/captureException/simple_error/subject.js b/packages/browser-integration-tests/suites/public-api/captureException/simple_error/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/captureException/simple_error/subject.js rename to packages/browser-integration-tests/suites/public-api/captureException/simple_error/subject.js diff --git a/packages/integration-tests/suites/public-api/captureException/simple_error/test.ts b/packages/browser-integration-tests/suites/public-api/captureException/simple_error/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/captureException/simple_error/test.ts rename to packages/browser-integration-tests/suites/public-api/captureException/simple_error/test.ts diff --git a/packages/integration-tests/suites/public-api/captureException/undefined_arg/subject.js b/packages/browser-integration-tests/suites/public-api/captureException/undefined_arg/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/captureException/undefined_arg/subject.js rename to packages/browser-integration-tests/suites/public-api/captureException/undefined_arg/subject.js diff --git a/packages/integration-tests/suites/public-api/captureException/undefined_arg/test.ts b/packages/browser-integration-tests/suites/public-api/captureException/undefined_arg/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/captureException/undefined_arg/test.ts rename to packages/browser-integration-tests/suites/public-api/captureException/undefined_arg/test.ts diff --git a/packages/integration-tests/suites/public-api/captureMessage/init.js b/packages/browser-integration-tests/suites/public-api/captureMessage/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/captureMessage/init.js rename to packages/browser-integration-tests/suites/public-api/captureMessage/init.js diff --git a/packages/integration-tests/suites/public-api/captureMessage/simple_message/subject.js b/packages/browser-integration-tests/suites/public-api/captureMessage/simple_message/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/captureMessage/simple_message/subject.js rename to packages/browser-integration-tests/suites/public-api/captureMessage/simple_message/subject.js diff --git a/packages/integration-tests/suites/public-api/captureMessage/simple_message/test.ts b/packages/browser-integration-tests/suites/public-api/captureMessage/simple_message/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/captureMessage/simple_message/test.ts rename to packages/browser-integration-tests/suites/public-api/captureMessage/simple_message/test.ts diff --git a/packages/integration-tests/suites/public-api/captureMessage/with_level/subject.js b/packages/browser-integration-tests/suites/public-api/captureMessage/with_level/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/captureMessage/with_level/subject.js rename to packages/browser-integration-tests/suites/public-api/captureMessage/with_level/subject.js diff --git a/packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts b/packages/browser-integration-tests/suites/public-api/captureMessage/with_level/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/captureMessage/with_level/test.ts rename to packages/browser-integration-tests/suites/public-api/captureMessage/with_level/test.ts diff --git a/packages/integration-tests/suites/public-api/configureScope/clear_scope/subject.js b/packages/browser-integration-tests/suites/public-api/configureScope/clear_scope/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/configureScope/clear_scope/subject.js rename to packages/browser-integration-tests/suites/public-api/configureScope/clear_scope/subject.js diff --git a/packages/integration-tests/suites/public-api/configureScope/clear_scope/test.ts b/packages/browser-integration-tests/suites/public-api/configureScope/clear_scope/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/configureScope/clear_scope/test.ts rename to packages/browser-integration-tests/suites/public-api/configureScope/clear_scope/test.ts diff --git a/packages/integration-tests/suites/public-api/configureScope/init.js b/packages/browser-integration-tests/suites/public-api/configureScope/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/configureScope/init.js rename to packages/browser-integration-tests/suites/public-api/configureScope/init.js diff --git a/packages/integration-tests/suites/public-api/configureScope/set_properties/subject.js b/packages/browser-integration-tests/suites/public-api/configureScope/set_properties/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/configureScope/set_properties/subject.js rename to packages/browser-integration-tests/suites/public-api/configureScope/set_properties/subject.js diff --git a/packages/integration-tests/suites/public-api/configureScope/set_properties/test.ts b/packages/browser-integration-tests/suites/public-api/configureScope/set_properties/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/configureScope/set_properties/test.ts rename to packages/browser-integration-tests/suites/public-api/configureScope/set_properties/test.ts diff --git a/packages/integration-tests/suites/public-api/init/console/test.ts b/packages/browser-integration-tests/suites/public-api/init/console/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/init/console/test.ts rename to packages/browser-integration-tests/suites/public-api/init/console/test.ts diff --git a/packages/integration-tests/suites/public-api/init/init.js b/packages/browser-integration-tests/suites/public-api/init/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/init/init.js rename to packages/browser-integration-tests/suites/public-api/init/init.js diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/subject.js b/packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/subject.js rename to packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/subject.js diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/test.ts b/packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/test.ts rename to packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/test.ts diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/subject.js b/packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/subject.js rename to packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/subject.js diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/test.ts b/packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/test.ts rename to packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/test.ts diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener-wrapping/subject.js b/packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-wrapping/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/instrumentation/eventListener-wrapping/subject.js rename to packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-wrapping/subject.js diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener-wrapping/test.ts b/packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-wrapping/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/instrumentation/eventListener-wrapping/test.ts rename to packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-wrapping/test.ts diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener/subject.js b/packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/instrumentation/eventListener/subject.js rename to packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/subject.js diff --git a/packages/integration-tests/suites/public-api/instrumentation/eventListener/test.ts b/packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/instrumentation/eventListener/test.ts rename to packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/test.ts diff --git a/packages/integration-tests/suites/public-api/instrumentation/init.js b/packages/browser-integration-tests/suites/public-api/instrumentation/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/instrumentation/init.js rename to packages/browser-integration-tests/suites/public-api/instrumentation/init.js diff --git a/packages/integration-tests/suites/public-api/setContext/init.js b/packages/browser-integration-tests/suites/public-api/setContext/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/setContext/init.js rename to packages/browser-integration-tests/suites/public-api/setContext/init.js diff --git a/packages/integration-tests/suites/public-api/setContext/multiple_contexts/subject.js b/packages/browser-integration-tests/suites/public-api/setContext/multiple_contexts/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setContext/multiple_contexts/subject.js rename to packages/browser-integration-tests/suites/public-api/setContext/multiple_contexts/subject.js diff --git a/packages/integration-tests/suites/public-api/setContext/multiple_contexts/test.ts b/packages/browser-integration-tests/suites/public-api/setContext/multiple_contexts/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setContext/multiple_contexts/test.ts rename to packages/browser-integration-tests/suites/public-api/setContext/multiple_contexts/test.ts diff --git a/packages/integration-tests/suites/public-api/setContext/non_serializable_context/subject.js b/packages/browser-integration-tests/suites/public-api/setContext/non_serializable_context/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setContext/non_serializable_context/subject.js rename to packages/browser-integration-tests/suites/public-api/setContext/non_serializable_context/subject.js diff --git a/packages/integration-tests/suites/public-api/setContext/non_serializable_context/test.ts b/packages/browser-integration-tests/suites/public-api/setContext/non_serializable_context/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setContext/non_serializable_context/test.ts rename to packages/browser-integration-tests/suites/public-api/setContext/non_serializable_context/test.ts diff --git a/packages/integration-tests/suites/public-api/setContext/simple_context/subject.js b/packages/browser-integration-tests/suites/public-api/setContext/simple_context/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setContext/simple_context/subject.js rename to packages/browser-integration-tests/suites/public-api/setContext/simple_context/subject.js diff --git a/packages/integration-tests/suites/public-api/setContext/simple_context/test.ts b/packages/browser-integration-tests/suites/public-api/setContext/simple_context/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setContext/simple_context/test.ts rename to packages/browser-integration-tests/suites/public-api/setContext/simple_context/test.ts diff --git a/packages/integration-tests/suites/public-api/setExtra/init.js b/packages/browser-integration-tests/suites/public-api/setExtra/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/setExtra/init.js rename to packages/browser-integration-tests/suites/public-api/setExtra/init.js diff --git a/packages/integration-tests/suites/public-api/setExtra/multiple_extras/subject.js b/packages/browser-integration-tests/suites/public-api/setExtra/multiple_extras/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setExtra/multiple_extras/subject.js rename to packages/browser-integration-tests/suites/public-api/setExtra/multiple_extras/subject.js diff --git a/packages/integration-tests/suites/public-api/setExtra/multiple_extras/test.ts b/packages/browser-integration-tests/suites/public-api/setExtra/multiple_extras/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setExtra/multiple_extras/test.ts rename to packages/browser-integration-tests/suites/public-api/setExtra/multiple_extras/test.ts diff --git a/packages/integration-tests/suites/public-api/setExtra/non_serializable_extra/subject.js b/packages/browser-integration-tests/suites/public-api/setExtra/non_serializable_extra/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setExtra/non_serializable_extra/subject.js rename to packages/browser-integration-tests/suites/public-api/setExtra/non_serializable_extra/subject.js diff --git a/packages/integration-tests/suites/public-api/setExtra/non_serializable_extra/test.ts b/packages/browser-integration-tests/suites/public-api/setExtra/non_serializable_extra/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setExtra/non_serializable_extra/test.ts rename to packages/browser-integration-tests/suites/public-api/setExtra/non_serializable_extra/test.ts diff --git a/packages/integration-tests/suites/public-api/setExtra/simple_extra/subject.js b/packages/browser-integration-tests/suites/public-api/setExtra/simple_extra/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setExtra/simple_extra/subject.js rename to packages/browser-integration-tests/suites/public-api/setExtra/simple_extra/subject.js diff --git a/packages/integration-tests/suites/public-api/setExtra/simple_extra/test.ts b/packages/browser-integration-tests/suites/public-api/setExtra/simple_extra/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setExtra/simple_extra/test.ts rename to packages/browser-integration-tests/suites/public-api/setExtra/simple_extra/test.ts diff --git a/packages/integration-tests/suites/public-api/setExtras/consecutive_calls/subject.js b/packages/browser-integration-tests/suites/public-api/setExtras/consecutive_calls/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setExtras/consecutive_calls/subject.js rename to packages/browser-integration-tests/suites/public-api/setExtras/consecutive_calls/subject.js diff --git a/packages/integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts b/packages/browser-integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts rename to packages/browser-integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts diff --git a/packages/integration-tests/suites/public-api/setExtras/init.js b/packages/browser-integration-tests/suites/public-api/setExtras/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/setExtras/init.js rename to packages/browser-integration-tests/suites/public-api/setExtras/init.js diff --git a/packages/integration-tests/suites/public-api/setExtras/multiple_extras/subject.js b/packages/browser-integration-tests/suites/public-api/setExtras/multiple_extras/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setExtras/multiple_extras/subject.js rename to packages/browser-integration-tests/suites/public-api/setExtras/multiple_extras/subject.js diff --git a/packages/integration-tests/suites/public-api/setExtras/multiple_extras/test.ts b/packages/browser-integration-tests/suites/public-api/setExtras/multiple_extras/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setExtras/multiple_extras/test.ts rename to packages/browser-integration-tests/suites/public-api/setExtras/multiple_extras/test.ts diff --git a/packages/integration-tests/suites/public-api/setTag/init.js b/packages/browser-integration-tests/suites/public-api/setTag/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/setTag/init.js rename to packages/browser-integration-tests/suites/public-api/setTag/init.js diff --git a/packages/integration-tests/suites/public-api/setTag/with_non_primitives/subject.js b/packages/browser-integration-tests/suites/public-api/setTag/with_non_primitives/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setTag/with_non_primitives/subject.js rename to packages/browser-integration-tests/suites/public-api/setTag/with_non_primitives/subject.js diff --git a/packages/integration-tests/suites/public-api/setTag/with_non_primitives/test.ts b/packages/browser-integration-tests/suites/public-api/setTag/with_non_primitives/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setTag/with_non_primitives/test.ts rename to packages/browser-integration-tests/suites/public-api/setTag/with_non_primitives/test.ts diff --git a/packages/integration-tests/suites/public-api/setTag/with_primitives/subject.js b/packages/browser-integration-tests/suites/public-api/setTag/with_primitives/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setTag/with_primitives/subject.js rename to packages/browser-integration-tests/suites/public-api/setTag/with_primitives/subject.js diff --git a/packages/integration-tests/suites/public-api/setTag/with_primitives/test.ts b/packages/browser-integration-tests/suites/public-api/setTag/with_primitives/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setTag/with_primitives/test.ts rename to packages/browser-integration-tests/suites/public-api/setTag/with_primitives/test.ts diff --git a/packages/integration-tests/suites/public-api/setTags/init.js b/packages/browser-integration-tests/suites/public-api/setTags/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/setTags/init.js rename to packages/browser-integration-tests/suites/public-api/setTags/init.js diff --git a/packages/integration-tests/suites/public-api/setTags/with_non_primitives/subject.js b/packages/browser-integration-tests/suites/public-api/setTags/with_non_primitives/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setTags/with_non_primitives/subject.js rename to packages/browser-integration-tests/suites/public-api/setTags/with_non_primitives/subject.js diff --git a/packages/integration-tests/suites/public-api/setTags/with_non_primitives/test.ts b/packages/browser-integration-tests/suites/public-api/setTags/with_non_primitives/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setTags/with_non_primitives/test.ts rename to packages/browser-integration-tests/suites/public-api/setTags/with_non_primitives/test.ts diff --git a/packages/integration-tests/suites/public-api/setTags/with_primitives/subject.js b/packages/browser-integration-tests/suites/public-api/setTags/with_primitives/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setTags/with_primitives/subject.js rename to packages/browser-integration-tests/suites/public-api/setTags/with_primitives/subject.js diff --git a/packages/integration-tests/suites/public-api/setTags/with_primitives/test.ts b/packages/browser-integration-tests/suites/public-api/setTags/with_primitives/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setTags/with_primitives/test.ts rename to packages/browser-integration-tests/suites/public-api/setTags/with_primitives/test.ts diff --git a/packages/integration-tests/suites/public-api/setUser/init.js b/packages/browser-integration-tests/suites/public-api/setUser/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/setUser/init.js rename to packages/browser-integration-tests/suites/public-api/setUser/init.js diff --git a/packages/integration-tests/suites/public-api/setUser/unset_user/subject.js b/packages/browser-integration-tests/suites/public-api/setUser/unset_user/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setUser/unset_user/subject.js rename to packages/browser-integration-tests/suites/public-api/setUser/unset_user/subject.js diff --git a/packages/integration-tests/suites/public-api/setUser/unset_user/test.ts b/packages/browser-integration-tests/suites/public-api/setUser/unset_user/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setUser/unset_user/test.ts rename to packages/browser-integration-tests/suites/public-api/setUser/unset_user/test.ts diff --git a/packages/integration-tests/suites/public-api/setUser/update_user/subject.js b/packages/browser-integration-tests/suites/public-api/setUser/update_user/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/setUser/update_user/subject.js rename to packages/browser-integration-tests/suites/public-api/setUser/update_user/subject.js diff --git a/packages/integration-tests/suites/public-api/setUser/update_user/test.ts b/packages/browser-integration-tests/suites/public-api/setUser/update_user/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/setUser/update_user/test.ts rename to packages/browser-integration-tests/suites/public-api/setUser/update_user/test.ts diff --git a/packages/integration-tests/suites/public-api/showReportDialog/init.js b/packages/browser-integration-tests/suites/public-api/showReportDialog/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/showReportDialog/init.js rename to packages/browser-integration-tests/suites/public-api/showReportDialog/init.js diff --git a/packages/integration-tests/suites/public-api/showReportDialog/inject-script/subject.js b/packages/browser-integration-tests/suites/public-api/showReportDialog/inject-script/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/showReportDialog/inject-script/subject.js rename to packages/browser-integration-tests/suites/public-api/showReportDialog/inject-script/subject.js diff --git a/packages/integration-tests/suites/public-api/showReportDialog/inject-script/test.ts b/packages/browser-integration-tests/suites/public-api/showReportDialog/inject-script/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/showReportDialog/inject-script/test.ts rename to packages/browser-integration-tests/suites/public-api/showReportDialog/inject-script/test.ts diff --git a/packages/integration-tests/suites/public-api/startTransaction/basic_usage/subject.js b/packages/browser-integration-tests/suites/public-api/startTransaction/basic_usage/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/startTransaction/basic_usage/subject.js rename to packages/browser-integration-tests/suites/public-api/startTransaction/basic_usage/subject.js diff --git a/packages/integration-tests/suites/public-api/startTransaction/basic_usage/test.ts b/packages/browser-integration-tests/suites/public-api/startTransaction/basic_usage/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/startTransaction/basic_usage/test.ts rename to packages/browser-integration-tests/suites/public-api/startTransaction/basic_usage/test.ts diff --git a/packages/integration-tests/suites/public-api/startTransaction/circular_data/subject.js b/packages/browser-integration-tests/suites/public-api/startTransaction/circular_data/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/startTransaction/circular_data/subject.js rename to packages/browser-integration-tests/suites/public-api/startTransaction/circular_data/subject.js diff --git a/packages/integration-tests/suites/public-api/startTransaction/circular_data/test.ts b/packages/browser-integration-tests/suites/public-api/startTransaction/circular_data/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/startTransaction/circular_data/test.ts rename to packages/browser-integration-tests/suites/public-api/startTransaction/circular_data/test.ts diff --git a/packages/integration-tests/suites/public-api/startTransaction/init.js b/packages/browser-integration-tests/suites/public-api/startTransaction/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/startTransaction/init.js rename to packages/browser-integration-tests/suites/public-api/startTransaction/init.js diff --git a/packages/integration-tests/suites/public-api/startTransaction/setMeasurement/subject.js b/packages/browser-integration-tests/suites/public-api/startTransaction/setMeasurement/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/startTransaction/setMeasurement/subject.js rename to packages/browser-integration-tests/suites/public-api/startTransaction/setMeasurement/subject.js diff --git a/packages/integration-tests/suites/public-api/startTransaction/setMeasurement/test.ts b/packages/browser-integration-tests/suites/public-api/startTransaction/setMeasurement/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/startTransaction/setMeasurement/test.ts rename to packages/browser-integration-tests/suites/public-api/startTransaction/setMeasurement/test.ts diff --git a/packages/integration-tests/suites/public-api/withScope/init.js b/packages/browser-integration-tests/suites/public-api/withScope/init.js similarity index 100% rename from packages/integration-tests/suites/public-api/withScope/init.js rename to packages/browser-integration-tests/suites/public-api/withScope/init.js diff --git a/packages/integration-tests/suites/public-api/withScope/nested_scopes/subject.js b/packages/browser-integration-tests/suites/public-api/withScope/nested_scopes/subject.js similarity index 100% rename from packages/integration-tests/suites/public-api/withScope/nested_scopes/subject.js rename to packages/browser-integration-tests/suites/public-api/withScope/nested_scopes/subject.js diff --git a/packages/integration-tests/suites/public-api/withScope/nested_scopes/test.ts b/packages/browser-integration-tests/suites/public-api/withScope/nested_scopes/test.ts similarity index 100% rename from packages/integration-tests/suites/public-api/withScope/nested_scopes/test.ts rename to packages/browser-integration-tests/suites/public-api/withScope/nested_scopes/test.ts diff --git a/packages/integration-tests/suites/replay/captureReplay/template.html b/packages/browser-integration-tests/suites/replay/captureReplay/template.html similarity index 100% rename from packages/integration-tests/suites/replay/captureReplay/template.html rename to packages/browser-integration-tests/suites/replay/captureReplay/template.html diff --git a/packages/integration-tests/suites/replay/captureReplay/test.ts b/packages/browser-integration-tests/suites/replay/captureReplay/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/captureReplay/test.ts rename to packages/browser-integration-tests/suites/replay/captureReplay/test.ts diff --git a/packages/integration-tests/suites/replay/captureReplayViaBrowser/init.js b/packages/browser-integration-tests/suites/replay/captureReplayViaBrowser/init.js similarity index 100% rename from packages/integration-tests/suites/replay/captureReplayViaBrowser/init.js rename to packages/browser-integration-tests/suites/replay/captureReplayViaBrowser/init.js diff --git a/packages/integration-tests/suites/replay/captureReplayViaBrowser/template.html b/packages/browser-integration-tests/suites/replay/captureReplayViaBrowser/template.html similarity index 100% rename from packages/integration-tests/suites/replay/captureReplayViaBrowser/template.html rename to packages/browser-integration-tests/suites/replay/captureReplayViaBrowser/template.html diff --git a/packages/integration-tests/suites/replay/captureReplayViaBrowser/test.ts b/packages/browser-integration-tests/suites/replay/captureReplayViaBrowser/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/captureReplayViaBrowser/test.ts rename to packages/browser-integration-tests/suites/replay/captureReplayViaBrowser/test.ts diff --git a/packages/integration-tests/suites/replay/compression/init.js b/packages/browser-integration-tests/suites/replay/compression/init.js similarity index 100% rename from packages/integration-tests/suites/replay/compression/init.js rename to packages/browser-integration-tests/suites/replay/compression/init.js diff --git a/packages/integration-tests/suites/replay/compression/subject.js b/packages/browser-integration-tests/suites/replay/compression/subject.js similarity index 100% rename from packages/integration-tests/suites/replay/compression/subject.js rename to packages/browser-integration-tests/suites/replay/compression/subject.js diff --git a/packages/integration-tests/suites/replay/compression/template.html b/packages/browser-integration-tests/suites/replay/compression/template.html similarity index 100% rename from packages/integration-tests/suites/replay/compression/template.html rename to packages/browser-integration-tests/suites/replay/compression/template.html diff --git a/packages/integration-tests/suites/replay/compression/test.ts b/packages/browser-integration-tests/suites/replay/compression/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/compression/test.ts rename to packages/browser-integration-tests/suites/replay/compression/test.ts diff --git a/packages/integration-tests/suites/replay/customEvents/init.js b/packages/browser-integration-tests/suites/replay/customEvents/init.js similarity index 100% rename from packages/integration-tests/suites/replay/customEvents/init.js rename to packages/browser-integration-tests/suites/replay/customEvents/init.js diff --git a/packages/integration-tests/suites/replay/customEvents/subject.js b/packages/browser-integration-tests/suites/replay/customEvents/subject.js similarity index 100% rename from packages/integration-tests/suites/replay/customEvents/subject.js rename to packages/browser-integration-tests/suites/replay/customEvents/subject.js diff --git a/packages/integration-tests/suites/replay/customEvents/template.html b/packages/browser-integration-tests/suites/replay/customEvents/template.html similarity index 100% rename from packages/integration-tests/suites/replay/customEvents/template.html rename to packages/browser-integration-tests/suites/replay/customEvents/template.html diff --git a/packages/integration-tests/suites/replay/customEvents/test.ts b/packages/browser-integration-tests/suites/replay/customEvents/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/customEvents/test.ts rename to packages/browser-integration-tests/suites/replay/customEvents/test.ts diff --git a/packages/integration-tests/suites/replay/errorResponse/template.html b/packages/browser-integration-tests/suites/replay/errorResponse/template.html similarity index 100% rename from packages/integration-tests/suites/replay/errorResponse/template.html rename to packages/browser-integration-tests/suites/replay/errorResponse/template.html diff --git a/packages/integration-tests/suites/replay/errorResponse/test.ts b/packages/browser-integration-tests/suites/replay/errorResponse/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/errorResponse/test.ts rename to packages/browser-integration-tests/suites/replay/errorResponse/test.ts diff --git a/packages/integration-tests/suites/replay/errors/droppedError/init.js b/packages/browser-integration-tests/suites/replay/errors/droppedError/init.js similarity index 100% rename from packages/integration-tests/suites/replay/errors/droppedError/init.js rename to packages/browser-integration-tests/suites/replay/errors/droppedError/init.js diff --git a/packages/integration-tests/suites/replay/errors/droppedError/test.ts b/packages/browser-integration-tests/suites/replay/errors/droppedError/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/errors/droppedError/test.ts rename to packages/browser-integration-tests/suites/replay/errors/droppedError/test.ts diff --git a/packages/integration-tests/suites/replay/errors/errorMode/test.ts b/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/errors/errorMode/test.ts rename to packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts diff --git a/packages/integration-tests/suites/replay/errors/errorsInSession/init.js b/packages/browser-integration-tests/suites/replay/errors/errorsInSession/init.js similarity index 100% rename from packages/integration-tests/suites/replay/errors/errorsInSession/init.js rename to packages/browser-integration-tests/suites/replay/errors/errorsInSession/init.js diff --git a/packages/integration-tests/suites/replay/errors/errorsInSession/test.ts b/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/errors/errorsInSession/test.ts rename to packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts diff --git a/packages/integration-tests/suites/replay/errors/init.js b/packages/browser-integration-tests/suites/replay/errors/init.js similarity index 100% rename from packages/integration-tests/suites/replay/errors/init.js rename to packages/browser-integration-tests/suites/replay/errors/init.js diff --git a/packages/integration-tests/suites/replay/errors/subject.js b/packages/browser-integration-tests/suites/replay/errors/subject.js similarity index 100% rename from packages/integration-tests/suites/replay/errors/subject.js rename to packages/browser-integration-tests/suites/replay/errors/subject.js diff --git a/packages/integration-tests/suites/replay/errors/template.html b/packages/browser-integration-tests/suites/replay/errors/template.html similarity index 100% rename from packages/integration-tests/suites/replay/errors/template.html rename to packages/browser-integration-tests/suites/replay/errors/template.html diff --git a/packages/integration-tests/suites/replay/flushing/init.js b/packages/browser-integration-tests/suites/replay/flushing/init.js similarity index 100% rename from packages/integration-tests/suites/replay/flushing/init.js rename to packages/browser-integration-tests/suites/replay/flushing/init.js diff --git a/packages/integration-tests/suites/replay/flushing/subject.js b/packages/browser-integration-tests/suites/replay/flushing/subject.js similarity index 100% rename from packages/integration-tests/suites/replay/flushing/subject.js rename to packages/browser-integration-tests/suites/replay/flushing/subject.js diff --git a/packages/integration-tests/suites/replay/flushing/template.html b/packages/browser-integration-tests/suites/replay/flushing/template.html similarity index 100% rename from packages/integration-tests/suites/replay/flushing/template.html rename to packages/browser-integration-tests/suites/replay/flushing/template.html diff --git a/packages/integration-tests/suites/replay/flushing/test.ts b/packages/browser-integration-tests/suites/replay/flushing/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/flushing/test.ts rename to packages/browser-integration-tests/suites/replay/flushing/test.ts diff --git a/packages/integration-tests/suites/replay/init.js b/packages/browser-integration-tests/suites/replay/init.js similarity index 100% rename from packages/integration-tests/suites/replay/init.js rename to packages/browser-integration-tests/suites/replay/init.js diff --git a/packages/integration-tests/suites/replay/multiple-pages/init.js b/packages/browser-integration-tests/suites/replay/multiple-pages/init.js similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/init.js rename to packages/browser-integration-tests/suites/replay/multiple-pages/init.js diff --git a/packages/integration-tests/suites/replay/multiple-pages/page-0.html b/packages/browser-integration-tests/suites/replay/multiple-pages/page-0.html similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/page-0.html rename to packages/browser-integration-tests/suites/replay/multiple-pages/page-0.html diff --git a/packages/integration-tests/suites/replay/multiple-pages/subject.js b/packages/browser-integration-tests/suites/replay/multiple-pages/subject.js similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/subject.js rename to packages/browser-integration-tests/suites/replay/multiple-pages/subject.js diff --git a/packages/integration-tests/suites/replay/multiple-pages/template.html b/packages/browser-integration-tests/suites/replay/multiple-pages/template.html similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/template.html rename to packages/browser-integration-tests/suites/replay/multiple-pages/template.html diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full-chromium b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full-chromium similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full-chromium rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-0-snap-full-chromium diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental-chromium b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental-chromium similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental-chromium rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-1-snap-incremental-chromium diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full-chromium b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full-chromium similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full-chromium rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-2-snap-full-chromium diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental-chromium b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental-chromium similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental-chromium rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-3-snap-incremental-chromium diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full-chromium b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full-chromium similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full-chromium rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-4-snap-full-chromium diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental-chromium b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental-chromium similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental-chromium rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-5-snap-incremental-chromium diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental-chromium b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental-chromium similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental-chromium rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-6-snap-incremental-chromium diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental-chromium b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental-chromium similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental-chromium rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-7-snap-incremental-chromium diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full-chromium b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full-chromium similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full-chromium rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-8-snap-full-chromium diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental diff --git a/packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental-chromium b/packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental-chromium similarity index 100% rename from packages/integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental-chromium rename to packages/browser-integration-tests/suites/replay/multiple-pages/test.ts-snapshots/seg-9-snap-incremental-chromium diff --git a/packages/integration-tests/suites/replay/privacyBlock/init.js b/packages/browser-integration-tests/suites/replay/privacyBlock/init.js similarity index 100% rename from packages/integration-tests/suites/replay/privacyBlock/init.js rename to packages/browser-integration-tests/suites/replay/privacyBlock/init.js diff --git a/packages/integration-tests/suites/replay/privacyBlock/template.html b/packages/browser-integration-tests/suites/replay/privacyBlock/template.html similarity index 100% rename from packages/integration-tests/suites/replay/privacyBlock/template.html rename to packages/browser-integration-tests/suites/replay/privacyBlock/template.html diff --git a/packages/integration-tests/suites/replay/privacyBlock/test.ts b/packages/browser-integration-tests/suites/replay/privacyBlock/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/privacyBlock/test.ts rename to packages/browser-integration-tests/suites/replay/privacyBlock/test.ts diff --git a/packages/integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy-chromium.json b/packages/browser-integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy-chromium.json similarity index 100% rename from packages/integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy-chromium.json rename to packages/browser-integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy-chromium.json diff --git a/packages/integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy-firefox.json b/packages/browser-integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy-firefox.json similarity index 100% rename from packages/integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy-firefox.json rename to packages/browser-integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy-firefox.json diff --git a/packages/integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy-webkit.json b/packages/browser-integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy-webkit.json similarity index 100% rename from packages/integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy-webkit.json rename to packages/browser-integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy-webkit.json diff --git a/packages/integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy.json b/packages/browser-integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy.json similarity index 100% rename from packages/integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy.json rename to packages/browser-integration-tests/suites/replay/privacyBlock/test.ts-snapshots/privacy.json diff --git a/packages/integration-tests/suites/replay/privacyDefault/init.js b/packages/browser-integration-tests/suites/replay/privacyDefault/init.js similarity index 100% rename from packages/integration-tests/suites/replay/privacyDefault/init.js rename to packages/browser-integration-tests/suites/replay/privacyDefault/init.js diff --git a/packages/integration-tests/suites/replay/privacyDefault/template.html b/packages/browser-integration-tests/suites/replay/privacyDefault/template.html similarity index 100% rename from packages/integration-tests/suites/replay/privacyDefault/template.html rename to packages/browser-integration-tests/suites/replay/privacyDefault/template.html diff --git a/packages/integration-tests/suites/replay/privacyDefault/test.ts b/packages/browser-integration-tests/suites/replay/privacyDefault/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/privacyDefault/test.ts rename to packages/browser-integration-tests/suites/replay/privacyDefault/test.ts diff --git a/packages/integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy-chromium.json b/packages/browser-integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy-chromium.json similarity index 100% rename from packages/integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy-chromium.json rename to packages/browser-integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy-chromium.json diff --git a/packages/integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy-firefox.json b/packages/browser-integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy-firefox.json similarity index 100% rename from packages/integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy-firefox.json rename to packages/browser-integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy-firefox.json diff --git a/packages/integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy-webkit.json b/packages/browser-integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy-webkit.json similarity index 100% rename from packages/integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy-webkit.json rename to packages/browser-integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy-webkit.json diff --git a/packages/integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy.json b/packages/browser-integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy.json similarity index 100% rename from packages/integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy.json rename to packages/browser-integration-tests/suites/replay/privacyDefault/test.ts-snapshots/privacy.json diff --git a/packages/integration-tests/suites/replay/privacyInput/init.js b/packages/browser-integration-tests/suites/replay/privacyInput/init.js similarity index 100% rename from packages/integration-tests/suites/replay/privacyInput/init.js rename to packages/browser-integration-tests/suites/replay/privacyInput/init.js diff --git a/packages/integration-tests/suites/replay/privacyInput/template.html b/packages/browser-integration-tests/suites/replay/privacyInput/template.html similarity index 100% rename from packages/integration-tests/suites/replay/privacyInput/template.html rename to packages/browser-integration-tests/suites/replay/privacyInput/template.html diff --git a/packages/integration-tests/suites/replay/privacyInput/test.ts b/packages/browser-integration-tests/suites/replay/privacyInput/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/privacyInput/test.ts rename to packages/browser-integration-tests/suites/replay/privacyInput/test.ts diff --git a/packages/integration-tests/suites/replay/privacyInputMaskAll/init.js b/packages/browser-integration-tests/suites/replay/privacyInputMaskAll/init.js similarity index 100% rename from packages/integration-tests/suites/replay/privacyInputMaskAll/init.js rename to packages/browser-integration-tests/suites/replay/privacyInputMaskAll/init.js diff --git a/packages/integration-tests/suites/replay/privacyInputMaskAll/template.html b/packages/browser-integration-tests/suites/replay/privacyInputMaskAll/template.html similarity index 100% rename from packages/integration-tests/suites/replay/privacyInputMaskAll/template.html rename to packages/browser-integration-tests/suites/replay/privacyInputMaskAll/template.html diff --git a/packages/integration-tests/suites/replay/privacyInputMaskAll/test.ts b/packages/browser-integration-tests/suites/replay/privacyInputMaskAll/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/privacyInputMaskAll/test.ts rename to packages/browser-integration-tests/suites/replay/privacyInputMaskAll/test.ts diff --git a/packages/integration-tests/suites/replay/replayShim/init.js b/packages/browser-integration-tests/suites/replay/replayShim/init.js similarity index 100% rename from packages/integration-tests/suites/replay/replayShim/init.js rename to packages/browser-integration-tests/suites/replay/replayShim/init.js diff --git a/packages/integration-tests/suites/replay/replayShim/template.html b/packages/browser-integration-tests/suites/replay/replayShim/template.html similarity index 100% rename from packages/integration-tests/suites/replay/replayShim/template.html rename to packages/browser-integration-tests/suites/replay/replayShim/template.html diff --git a/packages/integration-tests/suites/replay/replayShim/test.ts b/packages/browser-integration-tests/suites/replay/replayShim/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/replayShim/test.ts rename to packages/browser-integration-tests/suites/replay/replayShim/test.ts diff --git a/packages/integration-tests/suites/replay/requests/init.js b/packages/browser-integration-tests/suites/replay/requests/init.js similarity index 100% rename from packages/integration-tests/suites/replay/requests/init.js rename to packages/browser-integration-tests/suites/replay/requests/init.js diff --git a/packages/integration-tests/suites/replay/requests/subject.js b/packages/browser-integration-tests/suites/replay/requests/subject.js similarity index 100% rename from packages/integration-tests/suites/replay/requests/subject.js rename to packages/browser-integration-tests/suites/replay/requests/subject.js diff --git a/packages/integration-tests/suites/replay/requests/template.html b/packages/browser-integration-tests/suites/replay/requests/template.html similarity index 100% rename from packages/integration-tests/suites/replay/requests/template.html rename to packages/browser-integration-tests/suites/replay/requests/template.html diff --git a/packages/integration-tests/suites/replay/requests/test.ts b/packages/browser-integration-tests/suites/replay/requests/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/requests/test.ts rename to packages/browser-integration-tests/suites/replay/requests/test.ts diff --git a/packages/integration-tests/suites/replay/sampling/init.js b/packages/browser-integration-tests/suites/replay/sampling/init.js similarity index 100% rename from packages/integration-tests/suites/replay/sampling/init.js rename to packages/browser-integration-tests/suites/replay/sampling/init.js diff --git a/packages/integration-tests/suites/replay/sampling/template.html b/packages/browser-integration-tests/suites/replay/sampling/template.html similarity index 100% rename from packages/integration-tests/suites/replay/sampling/template.html rename to packages/browser-integration-tests/suites/replay/sampling/template.html diff --git a/packages/integration-tests/suites/replay/sampling/test.ts b/packages/browser-integration-tests/suites/replay/sampling/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/sampling/test.ts rename to packages/browser-integration-tests/suites/replay/sampling/test.ts diff --git a/packages/integration-tests/suites/replay/sessionExpiry/init.js b/packages/browser-integration-tests/suites/replay/sessionExpiry/init.js similarity index 100% rename from packages/integration-tests/suites/replay/sessionExpiry/init.js rename to packages/browser-integration-tests/suites/replay/sessionExpiry/init.js diff --git a/packages/integration-tests/suites/replay/sessionExpiry/template.html b/packages/browser-integration-tests/suites/replay/sessionExpiry/template.html similarity index 100% rename from packages/integration-tests/suites/replay/sessionExpiry/template.html rename to packages/browser-integration-tests/suites/replay/sessionExpiry/template.html diff --git a/packages/integration-tests/suites/replay/sessionExpiry/test.ts b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/sessionExpiry/test.ts rename to packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts diff --git a/packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-chromium.json b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-chromium.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-chromium.json rename to packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-chromium.json diff --git a/packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-firefox.json b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-firefox.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-firefox.json rename to packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-firefox.json diff --git a/packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-webkit.json b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-webkit.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-webkit.json rename to packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-webkit.json diff --git a/packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0.json b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0.json rename to packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0.json diff --git a/packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-chromium.json b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-chromium.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-chromium.json rename to packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-chromium.json diff --git a/packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-firefox.json b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-firefox.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-firefox.json rename to packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-firefox.json diff --git a/packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-webkit.json b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-webkit.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-webkit.json rename to packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-webkit.json diff --git a/packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2.json b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2.json rename to packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2.json diff --git a/packages/integration-tests/suites/replay/sessionInactive/init.js b/packages/browser-integration-tests/suites/replay/sessionInactive/init.js similarity index 100% rename from packages/integration-tests/suites/replay/sessionInactive/init.js rename to packages/browser-integration-tests/suites/replay/sessionInactive/init.js diff --git a/packages/integration-tests/suites/replay/sessionInactive/template.html b/packages/browser-integration-tests/suites/replay/sessionInactive/template.html similarity index 100% rename from packages/integration-tests/suites/replay/sessionInactive/template.html rename to packages/browser-integration-tests/suites/replay/sessionInactive/template.html diff --git a/packages/integration-tests/suites/replay/sessionInactive/test.ts b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/sessionInactive/test.ts rename to packages/browser-integration-tests/suites/replay/sessionInactive/test.ts diff --git a/packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-chromium.json b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-chromium.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-chromium.json rename to packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-chromium.json diff --git a/packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-firefox.json b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-firefox.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-firefox.json rename to packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-firefox.json diff --git a/packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-webkit.json b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-webkit.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-webkit.json rename to packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-webkit.json diff --git a/packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0.json b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0.json rename to packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0.json diff --git a/packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-chromium.json b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-chromium.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-chromium.json rename to packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-chromium.json diff --git a/packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-firefox.json b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-firefox.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-firefox.json rename to packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-firefox.json diff --git a/packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-webkit.json b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-webkit.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-webkit.json rename to packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-webkit.json diff --git a/packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1.json b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1.json rename to packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1.json diff --git a/packages/integration-tests/suites/replay/sessionMaxAge/init.js b/packages/browser-integration-tests/suites/replay/sessionMaxAge/init.js similarity index 100% rename from packages/integration-tests/suites/replay/sessionMaxAge/init.js rename to packages/browser-integration-tests/suites/replay/sessionMaxAge/init.js diff --git a/packages/integration-tests/suites/replay/sessionMaxAge/template.html b/packages/browser-integration-tests/suites/replay/sessionMaxAge/template.html similarity index 100% rename from packages/integration-tests/suites/replay/sessionMaxAge/template.html rename to packages/browser-integration-tests/suites/replay/sessionMaxAge/template.html diff --git a/packages/integration-tests/suites/replay/sessionMaxAge/test.ts b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/sessionMaxAge/test.ts rename to packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts diff --git a/packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-chromium.json b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-chromium.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-chromium.json rename to packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-chromium.json diff --git a/packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-firefox.json b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-firefox.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-firefox.json rename to packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-firefox.json diff --git a/packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-webkit.json b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-webkit.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-webkit.json rename to packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-webkit.json diff --git a/packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0.json b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0.json rename to packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0.json diff --git a/packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-chromium.json b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-chromium.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-chromium.json rename to packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-chromium.json diff --git a/packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-firefox.json b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-firefox.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-firefox.json rename to packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-firefox.json diff --git a/packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-webkit.json b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-webkit.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-webkit.json rename to packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-webkit.json diff --git a/packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2.json b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2.json similarity index 100% rename from packages/integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2.json rename to packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2.json diff --git a/packages/integration-tests/suites/replay/unicode/compressed/init.js b/packages/browser-integration-tests/suites/replay/unicode/compressed/init.js similarity index 100% rename from packages/integration-tests/suites/replay/unicode/compressed/init.js rename to packages/browser-integration-tests/suites/replay/unicode/compressed/init.js diff --git a/packages/integration-tests/suites/replay/unicode/compressed/test.ts b/packages/browser-integration-tests/suites/replay/unicode/compressed/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/unicode/compressed/test.ts rename to packages/browser-integration-tests/suites/replay/unicode/compressed/test.ts diff --git a/packages/integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-chromium.json b/packages/browser-integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-chromium.json similarity index 100% rename from packages/integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-chromium.json rename to packages/browser-integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-chromium.json diff --git a/packages/integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-firefox.json b/packages/browser-integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-firefox.json similarity index 100% rename from packages/integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-firefox.json rename to packages/browser-integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-firefox.json diff --git a/packages/integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-webkit.json b/packages/browser-integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-webkit.json similarity index 100% rename from packages/integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-webkit.json rename to packages/browser-integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed-webkit.json diff --git a/packages/integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed.json b/packages/browser-integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed.json similarity index 100% rename from packages/integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed.json rename to packages/browser-integration-tests/suites/replay/unicode/compressed/test.ts-snapshots/unicode-compressed.json diff --git a/packages/integration-tests/suites/replay/unicode/subject.js b/packages/browser-integration-tests/suites/replay/unicode/subject.js similarity index 100% rename from packages/integration-tests/suites/replay/unicode/subject.js rename to packages/browser-integration-tests/suites/replay/unicode/subject.js diff --git a/packages/integration-tests/suites/replay/unicode/template.html b/packages/browser-integration-tests/suites/replay/unicode/template.html similarity index 100% rename from packages/integration-tests/suites/replay/unicode/template.html rename to packages/browser-integration-tests/suites/replay/unicode/template.html diff --git a/packages/integration-tests/suites/replay/unicode/uncompressed/init.js b/packages/browser-integration-tests/suites/replay/unicode/uncompressed/init.js similarity index 100% rename from packages/integration-tests/suites/replay/unicode/uncompressed/init.js rename to packages/browser-integration-tests/suites/replay/unicode/uncompressed/init.js diff --git a/packages/integration-tests/suites/replay/unicode/uncompressed/test.ts b/packages/browser-integration-tests/suites/replay/unicode/uncompressed/test.ts similarity index 100% rename from packages/integration-tests/suites/replay/unicode/uncompressed/test.ts rename to packages/browser-integration-tests/suites/replay/unicode/uncompressed/test.ts diff --git a/packages/integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-chromium.json b/packages/browser-integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-chromium.json similarity index 100% rename from packages/integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-chromium.json rename to packages/browser-integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-chromium.json diff --git a/packages/integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-firefox.json b/packages/browser-integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-firefox.json similarity index 100% rename from packages/integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-firefox.json rename to packages/browser-integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-firefox.json diff --git a/packages/integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-webkit.json b/packages/browser-integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-webkit.json similarity index 100% rename from packages/integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-webkit.json rename to packages/browser-integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed-webkit.json diff --git a/packages/integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed.json b/packages/browser-integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed.json similarity index 100% rename from packages/integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed.json rename to packages/browser-integration-tests/suites/replay/unicode/uncompressed/test.ts-snapshots/unicode-uncompressed.json diff --git a/packages/integration-tests/suites/sessions/init.js b/packages/browser-integration-tests/suites/sessions/init.js similarity index 100% rename from packages/integration-tests/suites/sessions/init.js rename to packages/browser-integration-tests/suites/sessions/init.js diff --git a/packages/integration-tests/suites/sessions/start-session/template.html b/packages/browser-integration-tests/suites/sessions/start-session/template.html similarity index 100% rename from packages/integration-tests/suites/sessions/start-session/template.html rename to packages/browser-integration-tests/suites/sessions/start-session/template.html diff --git a/packages/integration-tests/suites/sessions/start-session/test.ts b/packages/browser-integration-tests/suites/sessions/start-session/test.ts similarity index 100% rename from packages/integration-tests/suites/sessions/start-session/test.ts rename to packages/browser-integration-tests/suites/sessions/start-session/test.ts diff --git a/packages/integration-tests/suites/sessions/update-session/subject.js b/packages/browser-integration-tests/suites/sessions/update-session/subject.js similarity index 100% rename from packages/integration-tests/suites/sessions/update-session/subject.js rename to packages/browser-integration-tests/suites/sessions/update-session/subject.js diff --git a/packages/integration-tests/suites/sessions/update-session/template.html b/packages/browser-integration-tests/suites/sessions/update-session/template.html similarity index 100% rename from packages/integration-tests/suites/sessions/update-session/template.html rename to packages/browser-integration-tests/suites/sessions/update-session/template.html diff --git a/packages/integration-tests/suites/sessions/update-session/test.ts b/packages/browser-integration-tests/suites/sessions/update-session/test.ts similarity index 100% rename from packages/integration-tests/suites/sessions/update-session/test.ts rename to packages/browser-integration-tests/suites/sessions/update-session/test.ts diff --git a/packages/integration-tests/suites/stacktraces/init.js b/packages/browser-integration-tests/suites/stacktraces/init.js similarity index 100% rename from packages/integration-tests/suites/stacktraces/init.js rename to packages/browser-integration-tests/suites/stacktraces/init.js diff --git a/packages/integration-tests/suites/stacktraces/protocol_containing_fn_identifiers/subject.js b/packages/browser-integration-tests/suites/stacktraces/protocol_containing_fn_identifiers/subject.js similarity index 100% rename from packages/integration-tests/suites/stacktraces/protocol_containing_fn_identifiers/subject.js rename to packages/browser-integration-tests/suites/stacktraces/protocol_containing_fn_identifiers/subject.js diff --git a/packages/integration-tests/suites/stacktraces/protocol_containing_fn_identifiers/test.ts b/packages/browser-integration-tests/suites/stacktraces/protocol_containing_fn_identifiers/test.ts similarity index 100% rename from packages/integration-tests/suites/stacktraces/protocol_containing_fn_identifiers/test.ts rename to packages/browser-integration-tests/suites/stacktraces/protocol_containing_fn_identifiers/test.ts diff --git a/packages/integration-tests/suites/stacktraces/protocol_fn_identifiers/subject.js b/packages/browser-integration-tests/suites/stacktraces/protocol_fn_identifiers/subject.js similarity index 100% rename from packages/integration-tests/suites/stacktraces/protocol_fn_identifiers/subject.js rename to packages/browser-integration-tests/suites/stacktraces/protocol_fn_identifiers/subject.js diff --git a/packages/integration-tests/suites/stacktraces/protocol_fn_identifiers/test.ts b/packages/browser-integration-tests/suites/stacktraces/protocol_fn_identifiers/test.ts similarity index 100% rename from packages/integration-tests/suites/stacktraces/protocol_fn_identifiers/test.ts rename to packages/browser-integration-tests/suites/stacktraces/protocol_fn_identifiers/test.ts diff --git a/packages/integration-tests/suites/stacktraces/regular_fn_identifiers/subject.js b/packages/browser-integration-tests/suites/stacktraces/regular_fn_identifiers/subject.js similarity index 100% rename from packages/integration-tests/suites/stacktraces/regular_fn_identifiers/subject.js rename to packages/browser-integration-tests/suites/stacktraces/regular_fn_identifiers/subject.js diff --git a/packages/integration-tests/suites/stacktraces/regular_fn_identifiers/test.ts b/packages/browser-integration-tests/suites/stacktraces/regular_fn_identifiers/test.ts similarity index 100% rename from packages/integration-tests/suites/stacktraces/regular_fn_identifiers/test.ts rename to packages/browser-integration-tests/suites/stacktraces/regular_fn_identifiers/test.ts diff --git a/packages/integration-tests/suites/stacktraces/template.html b/packages/browser-integration-tests/suites/stacktraces/template.html similarity index 100% rename from packages/integration-tests/suites/stacktraces/template.html rename to packages/browser-integration-tests/suites/stacktraces/template.html diff --git a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-custom/init.js b/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-custom/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/backgroundtab-custom/init.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-custom/init.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-custom/subject.js b/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-custom/subject.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/backgroundtab-custom/subject.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-custom/subject.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-custom/template.html b/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-custom/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/backgroundtab-custom/template.html rename to packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-custom/template.html diff --git a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-custom/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-custom/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/backgroundtab-custom/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-custom/test.ts diff --git a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/subject.js b/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/subject.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/subject.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/subject.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/template.html b/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/template.html rename to packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/template.html diff --git a/packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts diff --git a/packages/integration-tests/suites/tracing/browsertracing/init.js b/packages/browser-integration-tests/suites/tracing/browsertracing/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/init.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/init.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/interactions/assets/script.js b/packages/browser-integration-tests/suites/tracing/browsertracing/interactions/assets/script.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/interactions/assets/script.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/interactions/assets/script.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/interactions/init.js b/packages/browser-integration-tests/suites/tracing/browsertracing/interactions/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/interactions/init.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/interactions/init.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/interactions/template.html b/packages/browser-integration-tests/suites/tracing/browsertracing/interactions/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/interactions/template.html rename to packages/browser-integration-tests/suites/tracing/browsertracing/interactions/template.html diff --git a/packages/integration-tests/suites/tracing/browsertracing/interactions/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/interactions/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/interactions/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/interactions/test.ts diff --git a/packages/integration-tests/suites/tracing/browsertracing/long-tasks-disabled/assets/script.js b/packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-disabled/assets/script.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/long-tasks-disabled/assets/script.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-disabled/assets/script.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/long-tasks-disabled/init.js b/packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-disabled/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/long-tasks-disabled/init.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-disabled/init.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/long-tasks-disabled/template.html b/packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-disabled/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/long-tasks-disabled/template.html rename to packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-disabled/template.html diff --git a/packages/integration-tests/suites/tracing/browsertracing/long-tasks-disabled/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-disabled/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/long-tasks-disabled/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-disabled/test.ts diff --git a/packages/integration-tests/suites/tracing/browsertracing/long-tasks-enabled/assets/script.js b/packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-enabled/assets/script.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/long-tasks-enabled/assets/script.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-enabled/assets/script.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/long-tasks-enabled/init.js b/packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-enabled/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/long-tasks-enabled/init.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-enabled/init.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/long-tasks-enabled/template.html b/packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-enabled/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/long-tasks-enabled/template.html rename to packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-enabled/template.html diff --git a/packages/integration-tests/suites/tracing/browsertracing/long-tasks-enabled/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-enabled/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/long-tasks-enabled/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/long-tasks-enabled/test.ts diff --git a/packages/integration-tests/suites/tracing/browsertracing/meta/init.js b/packages/browser-integration-tests/suites/tracing/browsertracing/meta/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/meta/init.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/meta/init.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/meta/template.html b/packages/browser-integration-tests/suites/tracing/browsertracing/meta/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/meta/template.html rename to packages/browser-integration-tests/suites/tracing/browsertracing/meta/template.html diff --git a/packages/integration-tests/suites/tracing/browsertracing/meta/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/meta/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/meta/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/meta/test.ts diff --git a/packages/integration-tests/suites/tracing/browsertracing/navigation/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/navigation/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/navigation/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/navigation/test.ts diff --git a/packages/integration-tests/suites/tracing/browsertracing/pageload/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/pageload/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/pageload/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/pageload/test.ts diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargets/init.js b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargets/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargets/init.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargets/init.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargets/subject.js b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargets/subject.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargets/subject.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargets/subject.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargets/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargets/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargets/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargets/test.ts diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/init.js b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/init.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/init.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/subject.js b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/subject.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/subject.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/subject.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTargetsAndOrigins/test.ts diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/init.js b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/init.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/init.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/subject.js b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/subject.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/subject.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/subject.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/customTracingOrigins/test.ts diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/init.js b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/init.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/init.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/subject.js b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/subject.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/subject.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/subject.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsMatch/test.ts diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/init.js b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/init.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/init.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/subject.js b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/subject.js similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/subject.js rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/subject.js diff --git a/packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/test.ts rename to packages/browser-integration-tests/suites/tracing/browsertracing/tracePropagationTargets/defaultTargetsNoMatch/test.ts diff --git a/packages/integration-tests/suites/tracing/envelope-header-transaction-name/init.js b/packages/browser-integration-tests/suites/tracing/envelope-header-transaction-name/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/envelope-header-transaction-name/init.js rename to packages/browser-integration-tests/suites/tracing/envelope-header-transaction-name/init.js diff --git a/packages/integration-tests/suites/tracing/envelope-header-transaction-name/test.ts b/packages/browser-integration-tests/suites/tracing/envelope-header-transaction-name/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/envelope-header-transaction-name/test.ts rename to packages/browser-integration-tests/suites/tracing/envelope-header-transaction-name/test.ts diff --git a/packages/integration-tests/suites/tracing/envelope-header/init.js b/packages/browser-integration-tests/suites/tracing/envelope-header/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/envelope-header/init.js rename to packages/browser-integration-tests/suites/tracing/envelope-header/init.js diff --git a/packages/integration-tests/suites/tracing/envelope-header/test.ts b/packages/browser-integration-tests/suites/tracing/envelope-header/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/envelope-header/test.ts rename to packages/browser-integration-tests/suites/tracing/envelope-header/test.ts diff --git a/packages/integration-tests/suites/tracing/metrics/connection-rtt/template.html b/packages/browser-integration-tests/suites/tracing/metrics/connection-rtt/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/connection-rtt/template.html rename to packages/browser-integration-tests/suites/tracing/metrics/connection-rtt/template.html diff --git a/packages/integration-tests/suites/tracing/metrics/connection-rtt/test.ts b/packages/browser-integration-tests/suites/tracing/metrics/connection-rtt/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/connection-rtt/test.ts rename to packages/browser-integration-tests/suites/tracing/metrics/connection-rtt/test.ts diff --git a/packages/integration-tests/suites/tracing/metrics/init.js b/packages/browser-integration-tests/suites/tracing/metrics/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/init.js rename to packages/browser-integration-tests/suites/tracing/metrics/init.js diff --git a/packages/integration-tests/suites/tracing/metrics/pageload-browser-spans/test.ts b/packages/browser-integration-tests/suites/tracing/metrics/pageload-browser-spans/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/pageload-browser-spans/test.ts rename to packages/browser-integration-tests/suites/tracing/metrics/pageload-browser-spans/test.ts diff --git a/packages/integration-tests/suites/tracing/metrics/pageload-resource-spans/assets/image.svg b/packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/assets/image.svg similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/pageload-resource-spans/assets/image.svg rename to packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/assets/image.svg diff --git a/packages/integration-tests/suites/tracing/metrics/pageload-resource-spans/assets/script.js b/packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/assets/script.js similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/pageload-resource-spans/assets/script.js rename to packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/assets/script.js diff --git a/packages/integration-tests/suites/tracing/metrics/pageload-resource-spans/assets/style.css b/packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/assets/style.css similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/pageload-resource-spans/assets/style.css rename to packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/assets/style.css diff --git a/packages/integration-tests/suites/tracing/metrics/pageload-resource-spans/template.html b/packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/pageload-resource-spans/template.html rename to packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/template.html diff --git a/packages/integration-tests/suites/tracing/metrics/pageload-resource-spans/test.ts b/packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/pageload-resource-spans/test.ts rename to packages/browser-integration-tests/suites/tracing/metrics/pageload-resource-spans/test.ts diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-cls/subject.js b/packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls/subject.js similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/web-vitals-cls/subject.js rename to packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls/subject.js diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-cls/template.html b/packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/web-vitals-cls/template.html rename to packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls/template.html diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-cls/test.ts b/packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/web-vitals-cls/test.ts rename to packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls/test.ts diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-fid/template.html b/packages/browser-integration-tests/suites/tracing/metrics/web-vitals-fid/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/web-vitals-fid/template.html rename to packages/browser-integration-tests/suites/tracing/metrics/web-vitals-fid/template.html diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-fid/test.ts b/packages/browser-integration-tests/suites/tracing/metrics/web-vitals-fid/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/web-vitals-fid/test.ts rename to packages/browser-integration-tests/suites/tracing/metrics/web-vitals-fid/test.ts diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-fp-fcp/template.html b/packages/browser-integration-tests/suites/tracing/metrics/web-vitals-fp-fcp/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/web-vitals-fp-fcp/template.html rename to packages/browser-integration-tests/suites/tracing/metrics/web-vitals-fp-fcp/template.html diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-fp-fcp/test.ts b/packages/browser-integration-tests/suites/tracing/metrics/web-vitals-fp-fcp/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/web-vitals-fp-fcp/test.ts rename to packages/browser-integration-tests/suites/tracing/metrics/web-vitals-fp-fcp/test.ts diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-lcp/assets/sentry-logo-600x179.png b/packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/assets/sentry-logo-600x179.png similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/web-vitals-lcp/assets/sentry-logo-600x179.png rename to packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/assets/sentry-logo-600x179.png diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-lcp/template.html b/packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/web-vitals-lcp/template.html rename to packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/template.html diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts b/packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts rename to packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-ttfb/template.html b/packages/browser-integration-tests/suites/tracing/metrics/web-vitals-ttfb/template.html similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/web-vitals-ttfb/template.html rename to packages/browser-integration-tests/suites/tracing/metrics/web-vitals-ttfb/template.html diff --git a/packages/integration-tests/suites/tracing/metrics/web-vitals-ttfb/test.ts b/packages/browser-integration-tests/suites/tracing/metrics/web-vitals-ttfb/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/metrics/web-vitals-ttfb/test.ts rename to packages/browser-integration-tests/suites/tracing/metrics/web-vitals-ttfb/test.ts diff --git a/packages/integration-tests/suites/tracing/request/fetch/subject.js b/packages/browser-integration-tests/suites/tracing/request/fetch/subject.js similarity index 100% rename from packages/integration-tests/suites/tracing/request/fetch/subject.js rename to packages/browser-integration-tests/suites/tracing/request/fetch/subject.js diff --git a/packages/integration-tests/suites/tracing/request/fetch/test.ts b/packages/browser-integration-tests/suites/tracing/request/fetch/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/request/fetch/test.ts rename to packages/browser-integration-tests/suites/tracing/request/fetch/test.ts diff --git a/packages/integration-tests/suites/tracing/request/init.js b/packages/browser-integration-tests/suites/tracing/request/init.js similarity index 100% rename from packages/integration-tests/suites/tracing/request/init.js rename to packages/browser-integration-tests/suites/tracing/request/init.js diff --git a/packages/integration-tests/suites/tracing/request/xhr/subject.js b/packages/browser-integration-tests/suites/tracing/request/xhr/subject.js similarity index 100% rename from packages/integration-tests/suites/tracing/request/xhr/subject.js rename to packages/browser-integration-tests/suites/tracing/request/xhr/subject.js diff --git a/packages/integration-tests/suites/tracing/request/xhr/test.ts b/packages/browser-integration-tests/suites/tracing/request/xhr/test.ts similarity index 100% rename from packages/integration-tests/suites/tracing/request/xhr/test.ts rename to packages/browser-integration-tests/suites/tracing/request/xhr/test.ts diff --git a/packages/integration-tests/suites/transport/offline/init.js b/packages/browser-integration-tests/suites/transport/offline/init.js similarity index 100% rename from packages/integration-tests/suites/transport/offline/init.js rename to packages/browser-integration-tests/suites/transport/offline/init.js diff --git a/packages/integration-tests/suites/transport/offline/queued/subject.js b/packages/browser-integration-tests/suites/transport/offline/queued/subject.js similarity index 100% rename from packages/integration-tests/suites/transport/offline/queued/subject.js rename to packages/browser-integration-tests/suites/transport/offline/queued/subject.js diff --git a/packages/integration-tests/suites/transport/offline/queued/test.ts b/packages/browser-integration-tests/suites/transport/offline/queued/test.ts similarity index 100% rename from packages/integration-tests/suites/transport/offline/queued/test.ts rename to packages/browser-integration-tests/suites/transport/offline/queued/test.ts diff --git a/packages/integration-tests/suites/wasm/init.js b/packages/browser-integration-tests/suites/wasm/init.js similarity index 100% rename from packages/integration-tests/suites/wasm/init.js rename to packages/browser-integration-tests/suites/wasm/init.js diff --git a/packages/integration-tests/suites/wasm/simple.wasm b/packages/browser-integration-tests/suites/wasm/simple.wasm similarity index 100% rename from packages/integration-tests/suites/wasm/simple.wasm rename to packages/browser-integration-tests/suites/wasm/simple.wasm diff --git a/packages/integration-tests/suites/wasm/subject.js b/packages/browser-integration-tests/suites/wasm/subject.js similarity index 100% rename from packages/integration-tests/suites/wasm/subject.js rename to packages/browser-integration-tests/suites/wasm/subject.js diff --git a/packages/integration-tests/suites/wasm/test.ts b/packages/browser-integration-tests/suites/wasm/test.ts similarity index 100% rename from packages/integration-tests/suites/wasm/test.ts rename to packages/browser-integration-tests/suites/wasm/test.ts diff --git a/packages/integration-tests/tsconfig.json b/packages/browser-integration-tests/tsconfig.json similarity index 100% rename from packages/integration-tests/tsconfig.json rename to packages/browser-integration-tests/tsconfig.json diff --git a/packages/integration-tests/utils/defaults/template.html b/packages/browser-integration-tests/utils/defaults/template.html similarity index 100% rename from packages/integration-tests/utils/defaults/template.html rename to packages/browser-integration-tests/utils/defaults/template.html diff --git a/packages/integration-tests/utils/fixtures.ts b/packages/browser-integration-tests/utils/fixtures.ts similarity index 100% rename from packages/integration-tests/utils/fixtures.ts rename to packages/browser-integration-tests/utils/fixtures.ts diff --git a/packages/integration-tests/utils/generatePage.ts b/packages/browser-integration-tests/utils/generatePage.ts similarity index 100% rename from packages/integration-tests/utils/generatePage.ts rename to packages/browser-integration-tests/utils/generatePage.ts diff --git a/packages/integration-tests/utils/generatePlugin.ts b/packages/browser-integration-tests/utils/generatePlugin.ts similarity index 100% rename from packages/integration-tests/utils/generatePlugin.ts rename to packages/browser-integration-tests/utils/generatePlugin.ts diff --git a/packages/integration-tests/utils/helpers.ts b/packages/browser-integration-tests/utils/helpers.ts similarity index 100% rename from packages/integration-tests/utils/helpers.ts rename to packages/browser-integration-tests/utils/helpers.ts diff --git a/packages/integration-tests/utils/replayEventTemplates.ts b/packages/browser-integration-tests/utils/replayEventTemplates.ts similarity index 100% rename from packages/integration-tests/utils/replayEventTemplates.ts rename to packages/browser-integration-tests/utils/replayEventTemplates.ts diff --git a/packages/integration-tests/utils/replayHelpers.ts b/packages/browser-integration-tests/utils/replayHelpers.ts similarity index 100% rename from packages/integration-tests/utils/replayHelpers.ts rename to packages/browser-integration-tests/utils/replayHelpers.ts diff --git a/packages/integration-tests/utils/wasmHelpers.ts b/packages/browser-integration-tests/utils/wasmHelpers.ts similarity index 100% rename from packages/integration-tests/utils/wasmHelpers.ts rename to packages/browser-integration-tests/utils/wasmHelpers.ts diff --git a/packages/integration-tests/utils/web-vitals/cls.ts b/packages/browser-integration-tests/utils/web-vitals/cls.ts similarity index 100% rename from packages/integration-tests/utils/web-vitals/cls.ts rename to packages/browser-integration-tests/utils/web-vitals/cls.ts diff --git a/packages/integration-tests/webpack.config.ts b/packages/browser-integration-tests/webpack.config.ts similarity index 100% rename from packages/integration-tests/webpack.config.ts rename to packages/browser-integration-tests/webpack.config.ts diff --git a/packages/nextjs/test/integration/test/client/utils/helpers.ts b/packages/nextjs/test/integration/test/client/utils/helpers.ts index 38619935d908..7a00d2447aa9 100644 --- a/packages/nextjs/test/integration/test/client/utils/helpers.ts +++ b/packages/nextjs/test/integration/test/client/utils/helpers.ts @@ -1 +1 @@ -export * from '../../../../../../integration-tests/utils/helpers'; +export * from '../../../../../../browser-integration-tests/utils/helpers'; diff --git a/packages/remix/test/integration/test/client/utils/helpers.ts b/packages/remix/test/integration/test/client/utils/helpers.ts index 38619935d908..7a00d2447aa9 100644 --- a/packages/remix/test/integration/test/client/utils/helpers.ts +++ b/packages/remix/test/integration/test/client/utils/helpers.ts @@ -1 +1 @@ -export * from '../../../../../../integration-tests/utils/helpers'; +export * from '../../../../../../browser-integration-tests/utils/helpers'; From 09f326a7f3922e1af1ecb9c5ff83353ddd78a7e9 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 15 Mar 2023 12:07:58 +0100 Subject: [PATCH 17/54] chore: Add test rename to git blame ignore (#7467) --- .git-blame-ignore-revs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index ded035469e99..c70bdbf89d0c 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -6,3 +6,6 @@ # build: Add `@typescript-eslint/consistent-type-imports` rule (#6662) 2aa4e94b036675245290596884959e06dcced044 + +# chore: Rename `integration-tests` -> `browser-integration-tests` (#7455) +ef6b3c7877d5fc8031c08bb28b0ffafaeb01f501 \ No newline at end of file From d27c347b100f5fc1fdf47a37124e3093fe000400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kry=C5=A1tof=20Wold=C5=99ich?= <31292499+krystofwoldrich@users.noreply.github.com> Date: Wed, 15 Mar 2023 14:55:54 +0100 Subject: [PATCH 18/54] fix(core): Permanent idle timeout cancel finishes the transaction with the last finished child --- packages/core/src/tracing/idletransaction.ts | 4 +-- packages/tracing/test/idletransaction.test.ts | 34 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/core/src/tracing/idletransaction.ts b/packages/core/src/tracing/idletransaction.ts index 48fd2e50356e..f4604a312519 100644 --- a/packages/core/src/tracing/idletransaction.ts +++ b/packages/core/src/tracing/idletransaction.ts @@ -239,10 +239,10 @@ export class IdleTransaction extends Transaction { restartOnChildSpanChange: true, }, ): void { + this._idleTimeoutCanceledPermanently = restartOnChildSpanChange === false; if (this._idleTimeoutID) { clearTimeout(this._idleTimeoutID); this._idleTimeoutID = undefined; - this._idleTimeoutCanceledPermanently = restartOnChildSpanChange === false; if (Object.keys(this.activities).length === 0 && this._idleTimeoutCanceledPermanently) { this._finishReason = IDLE_TRANSACTION_FINISH_REASONS[5]; @@ -269,7 +269,7 @@ export class IdleTransaction extends Transaction { * @param spanId The span id that represents the activity */ private _pushActivity(spanId: string): void { - this.cancelIdleTimeout(); + this.cancelIdleTimeout(undefined, { restartOnChildSpanChange: !this._idleTimeoutCanceledPermanently }); __DEBUG_BUILD__ && logger.log(`[Tracing] pushActivity: ${spanId}`); this.activities[spanId] = true; __DEBUG_BUILD__ && logger.log('[Tracing] new activities count', Object.keys(this.activities).length); diff --git a/packages/tracing/test/idletransaction.test.ts b/packages/tracing/test/idletransaction.test.ts index 563c2220959c..a3382bacabbe 100644 --- a/packages/tracing/test/idletransaction.test.ts +++ b/packages/tracing/test/idletransaction.test.ts @@ -248,6 +248,40 @@ describe('IdleTransaction', () => { }); describe('cancelIdleTimeout', () => { + it('permanent idle timeout cancel is not restarted by child span start', () => { + const idleTimeout = 10; + const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub, idleTimeout); + transaction.initSpanRecorder(10); + + const firstSpan = transaction.startChild({}); + transaction.cancelIdleTimeout(undefined, { restartOnChildSpanChange: false }); + const secondSpan = transaction.startChild({}); + firstSpan.finish(); + secondSpan.finish(); + + expect(transaction.endTimestamp).toBeDefined(); + }); + + it('permanent idle timeout cancel finished the transaction with the last child', () => { + const idleTimeout = 10; + const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub, idleTimeout); + transaction.initSpanRecorder(10); + + const firstSpan = transaction.startChild({}); + transaction.cancelIdleTimeout(undefined, { restartOnChildSpanChange: false }); + const secondSpan = transaction.startChild({}); + const thirdSpan = transaction.startChild({}); + + firstSpan.finish(); + expect(transaction.endTimestamp).toBeUndefined(); + + secondSpan.finish(); + expect(transaction.endTimestamp).toBeUndefined(); + + thirdSpan.finish(); + expect(transaction.endTimestamp).toBeDefined(); + }); + it('permanent idle timeout cancel finishes transaction if there are no activities', () => { const idleTimeout = 10; const transaction = new IdleTransaction({ name: 'foo', startTimestamp: 1234 }, hub, idleTimeout); From 3269a06beb916147357714b4baeb0637470732da Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Wed, 15 Mar 2023 17:04:47 +0000 Subject: [PATCH 19/54] feat(tracing): Move Integrations from `tracing-internal` to `@sentry/tracing` (#7471) --- packages/tracing-internal/src/index.ts | 26 ++-------- .../src/node/integrations/index.ts | 4 -- packages/tracing/src/index.ts | 49 +++++++++++++++++-- 3 files changed, 48 insertions(+), 31 deletions(-) diff --git a/packages/tracing-internal/src/index.ts b/packages/tracing-internal/src/index.ts index 1f0143253d55..c735942fbe19 100644 --- a/packages/tracing-internal/src/index.ts +++ b/packages/tracing-internal/src/index.ts @@ -1,29 +1,9 @@ export * from './exports'; -import { addExtensionMethods } from './extensions'; -import * as Integrations from './node/integrations'; - -export { Integrations }; - -// BrowserTracing is already exported as part of `Integrations` above (and for the moment will remain so for -// backwards compatibility), but that interferes with treeshaking, so we also export it separately -// here. -// -// Previously we expected users to import tracing integrations like -// -// import { Integrations } from '@sentry/tracing'; -// const instance = new Integrations.BrowserTracing(); -// -// This makes the integrations unable to be treeshaken though. To address this, we now have -// this individual export. We now expect users to consume BrowserTracing like so: -// -// import { BrowserTracing } from '@sentry/tracing'; -// const instance = new BrowserTracing(); -// -// For an example of of the new usage of BrowserTracing, see @sentry/nextjs index.client.ts -export { BrowserTracing } from './browser'; +export { Apollo, Express, GraphQL, Mongo, Mysql, Postgres, Prisma } from './node/integrations'; export { + BrowserTracing, BROWSER_TRACING_INTEGRATION_ID, instrumentOutgoingRequests, defaultRequestInstrumentationOptions, @@ -31,4 +11,4 @@ export { export type { RequestInstrumentationOptions } from './browser'; -export { addExtensionMethods }; +export { addExtensionMethods } from './extensions'; diff --git a/packages/tracing-internal/src/node/integrations/index.ts b/packages/tracing-internal/src/node/integrations/index.ts index 91554d84fccd..607a3e129984 100644 --- a/packages/tracing-internal/src/node/integrations/index.ts +++ b/packages/tracing-internal/src/node/integrations/index.ts @@ -5,7 +5,3 @@ export { Mongo } from './mongo'; export { Prisma } from './prisma'; export { GraphQL } from './graphql'; export { Apollo } from './apollo'; - -// TODO(v8): Remove this export -// Please see `src/index.ts` for more details. -export { BrowserTracing } from '../../browser'; diff --git a/packages/tracing/src/index.ts b/packages/tracing/src/index.ts index 3f98af8cecd2..497806fd4660 100644 --- a/packages/tracing/src/index.ts +++ b/packages/tracing/src/index.ts @@ -1,6 +1,49 @@ -export * from '@sentry-internal/tracing'; +export { + // BrowserTracing is already exported as part of `Integrations` below (and for the moment will remain so for + // backwards compatibility), but that interferes with treeshaking, so we also export it separately + // here. + BrowserTracing, + BROWSER_TRACING_INTEGRATION_ID, + IdleTransaction, + Span, + // eslint-disable-next-line deprecation/deprecation + SpanStatus, + TRACEPARENT_REGEXP, + Transaction, + addExtensionMethods, + defaultRequestInstrumentationOptions, + extractTraceparentData, + instrumentOutgoingRequests, + getActiveTransaction, + hasTracingEnabled, + spanStatusfromHttpCode, + startIdleTransaction, + stripUrlQueryAndFragment, +} from '@sentry-internal/tracing'; +export type { RequestInstrumentationOptions, SpanStatusType } from '@sentry-internal/tracing'; -import { addExtensionMethods } from '@sentry-internal/tracing'; +import { + addExtensionMethods, + Apollo, + BrowserTracing, + Express, + GraphQL, + Mongo, + Mysql, + Postgres, + Prisma, +} from '@sentry-internal/tracing'; + +export const Integrations = { + BrowserTracing: BrowserTracing, + Apollo: Apollo, + Express: Express, + GraphQL: GraphQL, + Mongo: Mongo, + Mysql: Mysql, + Postgres: Postgres, + Prisma: Prisma, +}; // Treeshakable guard to remove all code related to tracing declare const __SENTRY_TRACING__: boolean; @@ -10,5 +53,3 @@ if (typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__) { // We are patching the global object with our hub extension methods addExtensionMethods(); } - -export { addExtensionMethods }; From 3715ae7ff71ffe43bf90279540ca93fc1be57098 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Thu, 16 Mar 2023 07:51:41 -0400 Subject: [PATCH 20/54] feat(replay): Add additional properties for UI clicks (#7395) For replay click breadcrumbs, add the following properties: * tag name * text content * element attributes Closes https://github.com/getsentry/sentry-javascript/issues/7394 --- .../suites/replay/customEvents/init.js | 1 + .../suites/replay/customEvents/template.html | 10 +- .../suites/replay/customEvents/test.ts | 91 ++++++++++++++++--- .../suites/replay/errors/errorMode/test.ts | 25 ++++- .../replay/errors/errorsInSession/test.ts | 41 ++++++++- .../utils/replayEventTemplates.ts | 8 ++ packages/replay/package.json | 1 + packages/replay/src/coreHandlers/handleDom.ts | 37 +++++--- .../util/getAttributesToRecord.ts | 33 +++++++ .../test/integration/errorSampleRate.test.ts | 2 +- .../util/getAttributesToRecord.test.ts | 32 +++++++ 11 files changed, 248 insertions(+), 33 deletions(-) create mode 100644 packages/replay/src/coreHandlers/util/getAttributesToRecord.ts create mode 100644 packages/replay/test/unit/coreHandlers/util/getAttributesToRecord.test.ts diff --git a/packages/browser-integration-tests/suites/replay/customEvents/init.js b/packages/browser-integration-tests/suites/replay/customEvents/init.js index db6a0aa21821..a850366eaebf 100644 --- a/packages/browser-integration-tests/suites/replay/customEvents/init.js +++ b/packages/browser-integration-tests/suites/replay/customEvents/init.js @@ -5,6 +5,7 @@ window.Replay = new Sentry.Replay({ flushMinDelay: 500, flushMaxDelay: 500, useCompression: false, + blockAllMedia: false, }); Sentry.init({ diff --git a/packages/browser-integration-tests/suites/replay/customEvents/template.html b/packages/browser-integration-tests/suites/replay/customEvents/template.html index 31cfc73ec3c3..56a956a95d24 100644 --- a/packages/browser-integration-tests/suites/replay/customEvents/template.html +++ b/packages/browser-integration-tests/suites/replay/customEvents/template.html @@ -4,6 +4,14 @@ - +
An Error
+ + diff --git a/packages/browser-integration-tests/suites/replay/customEvents/test.ts b/packages/browser-integration-tests/suites/replay/customEvents/test.ts index a0d790a5b62c..e64a76badea6 100644 --- a/packages/browser-integration-tests/suites/replay/customEvents/test.ts +++ b/packages/browser-integration-tests/suites/replay/customEvents/test.ts @@ -14,6 +14,7 @@ import type { PerformanceSpan } from '../../../utils/replayHelpers'; import { getCustomRecordingEvents, getReplayEvent, + getReplayRecordingContent, shouldSkipReplayTest, waitForReplayRequest, } from '../../../utils/replayHelpers'; @@ -81,8 +82,9 @@ sentryTest( sentryTest( 'replay recording should contain a click breadcrumb when a button is clicked', - async ({ getLocalTestPath, page }) => { - if (shouldSkipReplayTest()) { + async ({ forceFlushReplay, getLocalTestPath, page, browserName }) => { + // TODO(replay): This is flakey on firefox and webkit where clicks are flakey + if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { sentryTest.skip(); } @@ -100,21 +102,80 @@ sentryTest( const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - const replayEvent0 = getReplayEvent(await reqPromise0); - const { breadcrumbs: breadcrumbs0 } = getCustomRecordingEvents(await reqPromise0); - - expect(replayEvent0).toEqual(getExpectedReplayEvent({ segment_id: 0 })); - expect(breadcrumbs0.length).toEqual(0); - - await page.click('button'); - - const replayEvent1 = getReplayEvent(await reqPromise1); - const { breadcrumbs: breadcrumbs1 } = getCustomRecordingEvents(await reqPromise1); + await reqPromise0; + + await page.click('#error'); + await page.click('#img'); + await page.click('.sentry-unmask'); + await forceFlushReplay(); + const req1 = await reqPromise1; + const content1 = getReplayRecordingContent(req1); + expect(content1.breadcrumbs).toEqual( + expect.arrayContaining([ + { + ...expectedClickBreadcrumb, + message: 'body > div#error.btn.btn-error[aria-label="An Error"]', + data: { + nodeId: expect.any(Number), + node: { + attributes: { + 'aria-label': '** *****', + class: 'btn btn-error', + id: 'error', + role: 'button', + }, + id: expect.any(Number), + tagName: 'div', + textContent: '** *****', + }, + }, + }, + ]), + ); - expect(replayEvent1).toEqual( - getExpectedReplayEvent({ segment_id: 1, urls: [], replay_start_timestamp: undefined }), + expect(content1.breadcrumbs).toEqual( + expect.arrayContaining([ + { + ...expectedClickBreadcrumb, + message: 'body > button > img#img[alt="Alt Text"]', + data: { + nodeId: expect.any(Number), + node: { + attributes: { + alt: 'Alt Text', + id: 'img', + }, + id: expect.any(Number), + tagName: 'img', + textContent: '', + }, + }, + }, + ]), ); - expect(breadcrumbs1).toEqual([expectedClickBreadcrumb]); + expect(content1.breadcrumbs).toEqual( + expect.arrayContaining([ + { + ...expectedClickBreadcrumb, + message: 'body > button.sentry-unmask[aria-label="Unmasked label"]', + data: { + nodeId: expect.any(Number), + node: { + attributes: { + // TODO(rrweb): This is a bug in our rrweb fork! + // This attribute should be unmasked. + // 'aria-label': 'Unmasked label', + 'aria-label': '******** *****', + class: 'sentry-unmask', + }, + id: expect.any(Number), + tagName: 'button', + textContent: 'Unmasked', + }, + }, + }, + ]), + ); }, ); diff --git a/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts b/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts index b4da8aa4d3e4..826e04effc32 100644 --- a/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts +++ b/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts @@ -99,6 +99,17 @@ sentryTest( { ...expectedClickBreadcrumb, message: 'body > button#error', + data: { + nodeId: expect.any(Number), + node: { + attributes: { + id: 'error', + }, + id: expect.any(Number), + tagName: 'button', + textContent: '***** *****', + }, + }, }, ]), ); @@ -131,7 +142,19 @@ sentryTest( expect(content2.breadcrumbs).toEqual( expect.arrayContaining([ - { ...expectedClickBreadcrumb, message: 'body > button#log' }, + { + ...expectedClickBreadcrumb, + message: 'body > button#log', + data: { + node: { + attributes: { id: 'log' }, + id: expect.any(Number), + tagName: 'button', + textContent: '*** ***** ** *** *******', + }, + nodeId: expect.any(Number), + }, + }, { ...expectedConsoleBreadcrumb, level: 'log', message: 'Some message' }, ]), ); diff --git a/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts b/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts index 79835eef5771..80d36b6688e2 100644 --- a/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts +++ b/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts @@ -12,8 +12,9 @@ import { sentryTest( '[session-mode] replay event should contain an error id of an error that occurred during session recording', - async ({ getLocalTestPath, page }) => { - if (shouldSkipReplayTest()) { + async ({ getLocalTestPath, page, browserName }) => { + // TODO(replay): This is flakey on firefox where clicks are flakey + if (shouldSkipReplayTest() || ['firefox'].includes(browserName)) { sentryTest.skip(); } @@ -62,7 +63,23 @@ sentryTest( ); expect(content1.breadcrumbs).toEqual( - expect.arrayContaining([{ ...expectedClickBreadcrumb, message: 'body > button#error' }]), + expect.arrayContaining([ + { + ...expectedClickBreadcrumb, + message: 'body > button#error', + data: { + node: { + attributes: { + id: 'error', + }, + id: expect.any(Number), + tagName: 'button', + textContent: '***** *****', + }, + nodeId: expect.any(Number), + }, + }, + ]), ); }, ); @@ -108,7 +125,23 @@ sentryTest( // The button click that triggered the error should still be recorded expect(content1.breadcrumbs).toEqual( - expect.arrayContaining([{ ...expectedClickBreadcrumb, message: 'body > button#drop' }]), + expect.arrayContaining([ + { + ...expectedClickBreadcrumb, + message: 'body > button#drop', + data: { + node: { + attributes: { + id: 'drop', + }, + id: expect.any(Number), + tagName: 'button', + textContent: '***** ***** *** **** **', + }, + nodeId: expect.any(Number), + }, + }, + ]), ); }, ); diff --git a/packages/browser-integration-tests/utils/replayEventTemplates.ts b/packages/browser-integration-tests/utils/replayEventTemplates.ts index cabb27d5d6e9..c417624f9b9a 100644 --- a/packages/browser-integration-tests/utils/replayEventTemplates.ts +++ b/packages/browser-integration-tests/utils/replayEventTemplates.ts @@ -161,6 +161,14 @@ export const expectedClickBreadcrumb = { message: expect.any(String), data: { nodeId: expect.any(Number), + node: { + attributes: { + id: expect.any(String), + }, + id: expect.any(Number), + tagName: expect.any(String), + textContent: expect.any(String), + }, }, }; diff --git a/packages/replay/package.json b/packages/replay/package.json index 2a55e5500979..ccbbfb8bdef0 100644 --- a/packages/replay/package.json +++ b/packages/replay/package.json @@ -45,6 +45,7 @@ "@babel/core": "^7.17.5", "@sentry-internal/replay-worker": "7.43.0", "@sentry-internal/rrweb": "1.105.0", + "@sentry-internal/rrweb-snapshot": "1.105.0", "jsdom-worker": "^0.2.1", "tslib": "^1.9.3" }, diff --git a/packages/replay/src/coreHandlers/handleDom.ts b/packages/replay/src/coreHandlers/handleDom.ts index 17dcadc9d4ea..8d005afa1b46 100644 --- a/packages/replay/src/coreHandlers/handleDom.ts +++ b/packages/replay/src/coreHandlers/handleDom.ts @@ -1,10 +1,12 @@ -import { record } from '@sentry-internal/rrweb'; +import type { INode } from '@sentry-internal/rrweb-snapshot'; +import { NodeType } from '@sentry-internal/rrweb-snapshot'; import type { Breadcrumb } from '@sentry/types'; import { htmlTreeAsString } from '@sentry/utils'; import type { ReplayContainer } from '../types'; import { createBreadcrumb } from '../util/createBreadcrumb'; import { addBreadcrumbEvent } from './addBreadcrumbEvent'; +import { getAttributesToRecord } from './util/getAttributesToRecord'; interface DomHandlerData { name: string; @@ -31,9 +33,8 @@ export const handleDomListener: (replay: ReplayContainer) => (handlerData: DomHa * An event handler to react to DOM events. */ function handleDom(handlerData: DomHandlerData): Breadcrumb | null { - // Taken from https://github.com/getsentry/sentry-javascript/blob/master/packages/browser/src/integrations/breadcrumbs.ts#L112 let target; - let targetNode; + let targetNode: Node | INode | undefined; // Accessing event.target can throw (see getsentry/raven-js#838, #768) try { @@ -43,18 +44,32 @@ function handleDom(handlerData: DomHandlerData): Breadcrumb | null { target = ''; } - if (target.length === 0) { - return null; - } + // `__sn` property is the serialized node created by rrweb + const serializedNode = + targetNode && '__sn' in targetNode && targetNode.__sn.type === NodeType.Element ? targetNode.__sn : null; return createBreadcrumb({ category: `ui.${handlerData.name}`, message: target, - data: { - // Not sure why this errors, Node should be correct (Argument of type 'Node' is not assignable to parameter of type 'INode') - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ...(targetNode ? { nodeId: record.mirror.getId(targetNode as any) } : {}), - }, + data: serializedNode + ? { + nodeId: serializedNode.id, + node: { + id: serializedNode.id, + tagName: serializedNode.tagName, + textContent: targetNode + ? Array.from(targetNode.childNodes) + .map( + (node: Node | INode) => '__sn' in node && node.__sn.type === NodeType.Text && node.__sn.textContent, + ) + .filter(Boolean) // filter out empty values + .map(text => (text as string).trim()) + .join('') + : '', + attributes: getAttributesToRecord(serializedNode.attributes), + }, + } + : {}, }); } diff --git a/packages/replay/src/coreHandlers/util/getAttributesToRecord.ts b/packages/replay/src/coreHandlers/util/getAttributesToRecord.ts new file mode 100644 index 000000000000..7168a3243add --- /dev/null +++ b/packages/replay/src/coreHandlers/util/getAttributesToRecord.ts @@ -0,0 +1,33 @@ +// Note that these are the serialized attributes and not attributes directly on +// the DOM Node. Attributes we are interested in: +const ATTRIBUTES_TO_RECORD = new Set([ + 'id', + 'class', + 'aria-label', + 'role', + 'name', + 'alt', + 'title', + 'data-test-id', + 'data-testid', +]); + +/** + * Inclusion list of attributes that we want to record from the DOM element + */ +export function getAttributesToRecord(attributes: Record): Record { + const obj: Record = {}; + for (const key in attributes) { + if (ATTRIBUTES_TO_RECORD.has(key)) { + let normalizedKey = key; + + if (key === 'data-testid' || key === 'data-test-id') { + normalizedKey = 'testId'; + } + + obj[normalizedKey] = attributes[key]; + } + } + + return obj; +} diff --git a/packages/replay/test/integration/errorSampleRate.test.ts b/packages/replay/test/integration/errorSampleRate.test.ts index 1c4ef227db6c..d4bfa7279a95 100644 --- a/packages/replay/test/integration/errorSampleRate.test.ts +++ b/packages/replay/test/integration/errorSampleRate.test.ts @@ -54,7 +54,7 @@ describe('Integration | errorSampleRate', () => { expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled(); expect(replay).not.toHaveLastSentReplay(); - // Does not capture mouse click + // Does not capture on mouse click domHandler({ name: 'click', }); diff --git a/packages/replay/test/unit/coreHandlers/util/getAttributesToRecord.test.ts b/packages/replay/test/unit/coreHandlers/util/getAttributesToRecord.test.ts new file mode 100644 index 000000000000..46211820e2c7 --- /dev/null +++ b/packages/replay/test/unit/coreHandlers/util/getAttributesToRecord.test.ts @@ -0,0 +1,32 @@ +import { getAttributesToRecord } from '../../../../src/coreHandlers/util/getAttributesToRecord'; + +it('records only included attributes', function () { + expect( + getAttributesToRecord({ + id: 'foo', + class: 'btn btn-primary', + }), + ).toEqual({ + id: 'foo', + class: 'btn btn-primary', + }); + + expect( + getAttributesToRecord({ + id: 'foo', + class: 'btn btn-primary', + tabIndex: 2, + ariaDescribedBy: 'tooltip-1', + }), + ).toEqual({ + id: 'foo', + class: 'btn btn-primary', + }); + + expect( + getAttributesToRecord({ + tabIndex: 2, + ariaDescribedBy: 'tooltip-1', + }), + ).toEqual({}); +}); From dd40b4647db47d49c719e2893480a57328195793 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 16 Mar 2023 13:07:56 +0100 Subject: [PATCH 21/54] build(sveltekit): Remove `vite` types from build output (#7480) --- packages/sveltekit/.eslintrc.js | 6 ++++++ packages/sveltekit/package.json | 2 +- packages/sveltekit/tsconfig.json | 2 +- packages/sveltekit/tsconfig.test.json | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/sveltekit/.eslintrc.js b/packages/sveltekit/.eslintrc.js index 8fcc9ae58f9a..c3ca0faa363b 100644 --- a/packages/sveltekit/.eslintrc.js +++ b/packages/sveltekit/.eslintrc.js @@ -11,6 +11,12 @@ module.exports = { 'import/no-unresolved': 'off', }, }, + { + files: ['vite.config.ts'], + parserOptions: { + project: ['tsconfig.test.json'], + }, + }, ], extends: ['../../.eslintrc.js'], }; diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index 51e7c01b35d8..b1248af13f91 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -63,7 +63,7 @@ "lint:eslint": "eslint . --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "yarn test:unit", - "test:unit": "vitest", + "test:unit": "vitest run", "test:watch": "vitest --watch" }, "volta": { diff --git a/packages/sveltekit/tsconfig.json b/packages/sveltekit/tsconfig.json index 5282294db1db..bf45a09f2d71 100644 --- a/packages/sveltekit/tsconfig.json +++ b/packages/sveltekit/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.json", - "include": ["src/**/*", "vite.config.ts"], + "include": ["src/**/*"], "compilerOptions": { // package-specific options diff --git a/packages/sveltekit/tsconfig.test.json b/packages/sveltekit/tsconfig.test.json index ef8fa4c5130d..3fbe012384ee 100644 --- a/packages/sveltekit/tsconfig.test.json +++ b/packages/sveltekit/tsconfig.test.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*"], + "include": ["test/**/*", "vite.config.ts"], "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used From b27842973db9f4ad93cc02da1f555b852d7c06ce Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 16 Mar 2023 13:12:48 +0100 Subject: [PATCH 22/54] fix(serverless): Explicitly export node package exports (#7457) --- packages/serverless/src/index.ts | 46 ++++- packages/serverless/test/awslambda.test.ts | 82 ++++----- packages/serverless/test/awsservices.test.ts | 12 +- packages/serverless/test/gcpfunction.test.ts | 164 +++++++++--------- .../serverless/test/google-cloud-grpc.test.ts | 6 +- .../serverless/test/google-cloud-http.test.ts | 8 +- 6 files changed, 181 insertions(+), 137 deletions(-) diff --git a/packages/serverless/src/index.ts b/packages/serverless/src/index.ts index d3eabf5dc269..bd552060c6dd 100644 --- a/packages/serverless/src/index.ts +++ b/packages/serverless/src/index.ts @@ -3,5 +3,47 @@ import * as AWSLambda from './awslambda'; import * as GCPFunction from './gcpfunction'; export { AWSLambda, GCPFunction }; -export * from './awsservices'; -export * from '@sentry/node'; +export { AWSServices } from './awsservices'; + +// TODO(v8): We have to explicitly export these because of the namespace exports +// above. This is because just doing `export * from '@sentry/node'` will not +// work with Node native esm while we also have namespace exports in a package. +// What we should do is get rid of the namespace exports. +export { + Hub, + SDK_VERSION, + Scope, + addBreadcrumb, + addGlobalEventProcessor, + captureEvent, + captureException, + captureMessage, + configureScope, + createTransport, + getCurrentHub, + getHubFromCarrier, + makeMain, + setContext, + setExtra, + setExtras, + setTag, + setTags, + setUser, + startTransaction, + withScope, + NodeClient, + makeNodeTransport, + close, + defaultIntegrations, + defaultStackParser, + flush, + getSentryRelease, + init, + lastEventId, + DEFAULT_USER_INCLUDES, + addRequestDataToEvent, + extractRequestData, + deepReadDirSync, + Handlers, + Integrations, +} from '@sentry/node'; diff --git a/packages/serverless/test/awslambda.test.ts b/packages/serverless/test/awslambda.test.ts index 36b3ed5626e6..f2cff6e9b9c4 100644 --- a/packages/serverless/test/awslambda.test.ts +++ b/packages/serverless/test/awslambda.test.ts @@ -1,5 +1,7 @@ // NOTE: I have no idea how to fix this right now, and don't want to waste more time, as it builds just fine — Kamil // eslint-disable-next-line import/no-unresolved +import * as SentryNode from '@sentry/node'; +// eslint-disable-next-line import/no-unresolved import type { Callback, Handler } from 'aws-lambda'; import * as Sentry from '../src'; @@ -40,15 +42,15 @@ const fakeCallback: Callback = (err, result) => { function expectScopeSettings(fakeTransactionContext: any) { // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setTag).toBeCalledWith('server_name', expect.anything()); + expect(SentryNode.fakeScope.setTag).toBeCalledWith('server_name', expect.anything()); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setTag).toBeCalledWith('url', 'awslambda:///functionName'); + expect(SentryNode.fakeScope.setTag).toBeCalledWith('url', 'awslambda:///functionName'); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setContext).toBeCalledWith( + expect(SentryNode.fakeScope.setContext).toBeCalledWith( 'aws.lambda', expect.objectContaining({ aws_request_id: 'awsRequestId', @@ -59,7 +61,7 @@ function expectScopeSettings(fakeTransactionContext: any) { }), ); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setContext).toBeCalledWith( + expect(SentryNode.fakeScope.setContext).toBeCalledWith( 'aws.cloudwatch.logs', expect.objectContaining({ log_group: 'logGroupName', @@ -77,7 +79,7 @@ describe('AWSLambda', () => { afterEach(() => { // @ts-ignore see "Why @ts-ignore" note - Sentry.resetMocks(); + SentryNode.resetMocks(); }); describe('wrapHandler() options', () => { @@ -88,7 +90,7 @@ describe('AWSLambda', () => { const wrappedHandler = wrapHandler(handler, { flushTimeout: 1337 }); await wrappedHandler(fakeEvent, fakeContext, fakeCallback); - expect(Sentry.flush).toBeCalledWith(1337); + expect(SentryNode.flush).toBeCalledWith(1337); }); test('captureTimeoutWarning enabled (default)', async () => { @@ -104,7 +106,7 @@ describe('AWSLambda', () => { expect(Sentry.captureMessage).toBeCalled(); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setTag).toBeCalledWith('timeout', '1s'); + expect(SentryNode.fakeScope.setTag).toBeCalledWith('timeout', '1s'); }); test('captureTimeoutWarning disabled', async () => { @@ -152,14 +154,14 @@ describe('AWSLambda', () => { expect(Sentry.captureMessage).toBeCalled(); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setTag).toBeCalledWith('timeout', '1m40s'); + expect(SentryNode.fakeScope.setTag).toBeCalledWith('timeout', '1m40s'); }); test('captureAllSettledReasons disabled (default)', async () => { const handler = () => Promise.resolve([{ status: 'rejected', reason: new Error() }]); const wrappedHandler = wrapHandler(handler, { flushTimeout: 1337 }); await wrappedHandler(fakeEvent, fakeContext, fakeCallback); - expect(Sentry.captureException).toBeCalledTimes(0); + expect(SentryNode.captureException).toBeCalledTimes(0); }); test('captureAllSettledReasons enable', async () => { @@ -173,9 +175,9 @@ describe('AWSLambda', () => { ]); const wrappedHandler = wrapHandler(handler, { flushTimeout: 1337, captureAllSettledReasons: true }); await wrappedHandler(fakeEvent, fakeContext, fakeCallback); - expect(Sentry.captureException).toHaveBeenNthCalledWith(1, error); - expect(Sentry.captureException).toHaveBeenNthCalledWith(2, error2); - expect(Sentry.captureException).toBeCalledTimes(2); + expect(SentryNode.captureException).toHaveBeenNthCalledWith(1, error); + expect(SentryNode.captureException).toHaveBeenNthCalledWith(2, error2); + expect(SentryNode.captureException).toBeCalledTimes(2); }); }); @@ -197,11 +199,11 @@ describe('AWSLambda', () => { expect(rv).toStrictEqual(42); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); expectScopeSettings(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalledWith(2000); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalledWith(2000); }); test('unsuccessful execution', async () => { @@ -223,12 +225,12 @@ describe('AWSLambda', () => { }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); expectScopeSettings(fakeTransactionContext); - expect(Sentry.captureException).toBeCalledWith(error); + expect(SentryNode.captureException).toBeCalledWith(error); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalledWith(2000); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalledWith(2000); } }); @@ -254,7 +256,7 @@ describe('AWSLambda', () => { const handler: Handler = (_event, _context, callback) => { // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith( + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith( expect.objectContaining({ parentSpanId: '1121201211212012', parentSampled: false, @@ -300,12 +302,12 @@ describe('AWSLambda', () => { }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); expectScopeSettings(fakeTransactionContext); - expect(Sentry.captureException).toBeCalledWith(e); + expect(SentryNode.captureException).toBeCalledWith(e); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalled(); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalled(); } }); }); @@ -328,11 +330,11 @@ describe('AWSLambda', () => { expect(rv).toStrictEqual(42); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); expectScopeSettings(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalled(); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalled(); }); test('event and context are correctly passed to the original handler', async () => { @@ -365,12 +367,12 @@ describe('AWSLambda', () => { }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); expectScopeSettings(fakeTransactionContext); - expect(Sentry.captureException).toBeCalledWith(error); + expect(SentryNode.captureException).toBeCalledWith(error); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalled(); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalled(); } }); @@ -408,11 +410,11 @@ describe('AWSLambda', () => { expect(rv).toStrictEqual(42); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); expectScopeSettings(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalled(); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalled(); }); test('event and context are correctly passed to the original handler', async () => { @@ -445,12 +447,12 @@ describe('AWSLambda', () => { }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); expectScopeSettings(fakeTransactionContext); - expect(Sentry.captureException).toBeCalledWith(error); + expect(SentryNode.captureException).toBeCalledWith(error); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalled(); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalled(); } }); }); diff --git a/packages/serverless/test/awsservices.test.ts b/packages/serverless/test/awsservices.test.ts index fd1be7a74689..63051773ea04 100644 --- a/packages/serverless/test/awsservices.test.ts +++ b/packages/serverless/test/awsservices.test.ts @@ -1,7 +1,7 @@ +import * as SentryNode from '@sentry/node'; import * as AWS from 'aws-sdk'; import * as nock from 'nock'; -import * as Sentry from '../src'; import { AWSServices } from '../src/awsservices'; /** @@ -17,7 +17,7 @@ describe('AWSServices', () => { }); afterEach(() => { // @ts-ignore see "Why @ts-ignore" note - Sentry.resetMocks(); + SentryNode.resetMocks(); }); afterAll(() => { nock.restore(); @@ -31,12 +31,12 @@ describe('AWSServices', () => { const data = await s3.getObject({ Bucket: 'foo', Key: 'bar' }).promise(); expect(data.Body?.toString('utf-8')).toEqual('contents'); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.startChild).toBeCalledWith({ + expect(SentryNode.fakeTransaction.startChild).toBeCalledWith({ op: 'http.client', description: 'aws.s3.getObject foo', }); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeSpan.finish).toBeCalled(); + expect(SentryNode.fakeSpan.finish).toBeCalled(); }); test('getObject with callback', done => { @@ -48,7 +48,7 @@ describe('AWSServices', () => { done(); }); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.startChild).toBeCalledWith({ + expect(SentryNode.fakeTransaction.startChild).toBeCalledWith({ op: 'http.client', description: 'aws.s3.getObject foo', }); @@ -63,7 +63,7 @@ describe('AWSServices', () => { const data = await lambda.invoke({ FunctionName: 'foo' }).promise(); expect(data.Payload?.toString('utf-8')).toEqual('reply'); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.startChild).toBeCalledWith({ + expect(SentryNode.fakeTransaction.startChild).toBeCalledWith({ op: 'http.client', description: 'aws.lambda.invoke foo', }); diff --git a/packages/serverless/test/gcpfunction.test.ts b/packages/serverless/test/gcpfunction.test.ts index a6a58ebb2d8b..a203ceef4797 100644 --- a/packages/serverless/test/gcpfunction.test.ts +++ b/packages/serverless/test/gcpfunction.test.ts @@ -23,7 +23,7 @@ import type { describe('GCPFunction', () => { afterEach(() => { // @ts-ignore see "Why @ts-ignore" note - Sentry.resetMocks(); + SentryNode.resetMocks(); }); async function handleHttp(fn: HttpFunction, trace_headers: { [key: string]: string } | null = null): Promise { @@ -96,7 +96,7 @@ describe('GCPFunction', () => { const wrappedHandler = wrapHttpFunction(handler, { flushTimeout: 1337 }); await handleHttp(wrappedHandler); - expect(Sentry.flush).toBeCalledWith(1337); + expect(SentryNode.flush).toBeCalledWith(1337); }); }); @@ -117,17 +117,17 @@ describe('GCPFunction', () => { metadata: { source: 'route' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.setHttpStatus).toBeCalledWith(200); + expect(SentryNode.fakeTransaction.setHttpStatus).toBeCalledWith(200); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalledWith(2000); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalledWith(2000); }); test('incoming trace headers are correctly parsed and used', async () => { @@ -159,10 +159,10 @@ describe('GCPFunction', () => { }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - // expect(Sentry.fakeHub.startTransaction).toBeCalledWith(expect.objectContaining(fakeTransactionContext)); + // expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(expect.objectContaining(fakeTransactionContext)); }); test('capture error', async () => { @@ -189,16 +189,16 @@ describe('GCPFunction', () => { metadata: { dynamicSamplingContext: {}, source: 'route' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); - expect(Sentry.captureException).toBeCalledWith(error); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.captureException).toBeCalledWith(error); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalled(); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalled(); }); test('should not throw when flush rejects', async () => { @@ -252,7 +252,7 @@ describe('GCPFunction', () => { ); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSDKProcessingMetadata).toHaveBeenCalledWith({ + expect(SentryNode.fakeScope.setSDKProcessingMetadata).toHaveBeenCalledWith({ request: { method: 'POST', url: '/path?q=query', @@ -279,15 +279,15 @@ describe('GCPFunction', () => { metadata: { source: 'component' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalledWith(2000); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalledWith(2000); }); test('capture error', async () => { @@ -306,16 +306,16 @@ describe('GCPFunction', () => { metadata: { source: 'component' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); - expect(Sentry.captureException).toBeCalledWith(error); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.captureException).toBeCalledWith(error); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalled(); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalled(); }); }); @@ -338,15 +338,15 @@ describe('GCPFunction', () => { metadata: { source: 'component' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalledWith(2000); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalledWith(2000); }); test('capture error', async () => { @@ -369,16 +369,16 @@ describe('GCPFunction', () => { metadata: { source: 'component' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); - expect(Sentry.captureException).toBeCalledWith(error); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.captureException).toBeCalledWith(error); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalled(); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalled(); }); }); @@ -398,15 +398,15 @@ describe('GCPFunction', () => { metadata: { source: 'component' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalledWith(2000); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalledWith(2000); }); test('capture error', async () => { @@ -425,16 +425,16 @@ describe('GCPFunction', () => { metadata: { source: 'component' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); - expect(Sentry.captureException).toBeCalledWith(error); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.captureException).toBeCalledWith(error); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalled(); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalled(); }); test('capture exception', async () => { @@ -453,13 +453,13 @@ describe('GCPFunction', () => { metadata: { source: 'component' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); - expect(Sentry.captureException).toBeCalledWith(error); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.captureException).toBeCalledWith(error); }); }); @@ -470,7 +470,7 @@ describe('GCPFunction', () => { const wrappedHandler = wrapEventFunction(handler); await handleEvent(wrappedHandler); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setContext).toBeCalledWith('gcp.function.context', { + expect(SentryNode.fakeScope.setContext).toBeCalledWith('gcp.function.context', { eventType: 'event.type', resource: 'some.resource', }); @@ -492,15 +492,15 @@ describe('GCPFunction', () => { metadata: { source: 'component' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalledWith(2000); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalledWith(2000); }); test('capture error', async () => { @@ -519,16 +519,16 @@ describe('GCPFunction', () => { metadata: { source: 'component' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); - expect(Sentry.captureException).toBeCalledWith(error); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.captureException).toBeCalledWith(error); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalled(); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalled(); }); }); @@ -548,15 +548,15 @@ describe('GCPFunction', () => { metadata: { source: 'component' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalledWith(2000); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalledWith(2000); }); test('capture error', async () => { @@ -575,16 +575,16 @@ describe('GCPFunction', () => { metadata: { source: 'component' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); - expect(Sentry.captureException).toBeCalledWith(error); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.captureException).toBeCalledWith(error); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.finish).toBeCalled(); - expect(Sentry.flush).toBeCalled(); + expect(SentryNode.fakeTransaction.finish).toBeCalled(); + expect(SentryNode.flush).toBeCalled(); }); test('capture exception', async () => { @@ -603,14 +603,14 @@ describe('GCPFunction', () => { metadata: { source: 'component' }, }; // @ts-ignore see "Why @ts-ignore" note - const fakeTransaction = { ...Sentry.fakeTransaction, ...fakeTransactionContext }; + const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext }; // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); + expect(SentryNode.fakeHub.startTransaction).toBeCalledWith(fakeTransactionContext); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setSpan).toBeCalledWith(fakeTransaction); + expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction); - expect(Sentry.captureException).toBeCalledWith(error); + expect(SentryNode.captureException).toBeCalledWith(error); }); }); @@ -621,7 +621,7 @@ describe('GCPFunction', () => { const wrappedHandler = wrapCloudEventFunction(handler); await handleCloudEvent(wrappedHandler); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeScope.setContext).toBeCalledWith('gcp.function.context', { type: 'event.type' }); + expect(SentryNode.fakeScope.setContext).toBeCalledWith('gcp.function.context', { type: 'event.type' }); }); describe('init()', () => { diff --git a/packages/serverless/test/google-cloud-grpc.test.ts b/packages/serverless/test/google-cloud-grpc.test.ts index ffd0d0144346..ec8dcfe8c77b 100644 --- a/packages/serverless/test/google-cloud-grpc.test.ts +++ b/packages/serverless/test/google-cloud-grpc.test.ts @@ -1,6 +1,7 @@ jest.mock('dns'); import { PubSub } from '@google-cloud/pubsub'; +import * as SentryNode from '@sentry/node'; import * as dns from 'dns'; import { EventEmitter } from 'events'; import * as fs from 'fs'; @@ -8,7 +9,6 @@ import * as http2 from 'http2'; import * as nock from 'nock'; import * as path from 'path'; -import * as Sentry from '../src'; import { GoogleCloudGrpc } from '../src/google-cloud-grpc'; /** @@ -86,7 +86,7 @@ describe('GoogleCloudGrpc tracing', () => { }); afterEach(() => { // @ts-ignore see "Why @ts-ignore" note - Sentry.resetMocks(); + SentryNode.resetMocks(); spyConnect.mockClear(); }); afterAll(() => { @@ -127,7 +127,7 @@ describe('GoogleCloudGrpc tracing', () => { const resp = await pubsub.topic('nicetopic').publish(Buffer.from('data')); expect(resp).toEqual('1637084156623860'); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.startChild).toBeCalledWith({ + expect(SentryNode.fakeTransaction.startChild).toBeCalledWith({ op: 'grpc.pubsub', description: 'unary call publish', }); diff --git a/packages/serverless/test/google-cloud-http.test.ts b/packages/serverless/test/google-cloud-http.test.ts index 7a34d5026a5b..f1fb19bad9ac 100644 --- a/packages/serverless/test/google-cloud-http.test.ts +++ b/packages/serverless/test/google-cloud-http.test.ts @@ -1,9 +1,9 @@ import { BigQuery } from '@google-cloud/bigquery'; +import * as SentryNode from '@sentry/node'; import * as fs from 'fs'; import * as nock from 'nock'; import * as path from 'path'; -import * as Sentry from '../src'; import { GoogleCloudHttp } from '../src/google-cloud-http'; /** @@ -24,7 +24,7 @@ describe('GoogleCloudHttp tracing', () => { }); afterEach(() => { // @ts-ignore see "Why @ts-ignore" note - Sentry.resetMocks(); + SentryNode.resetMocks(); }); afterAll(() => { nock.restore(); @@ -58,12 +58,12 @@ describe('GoogleCloudHttp tracing', () => { const resp = await bigquery.query('SELECT true AS foo'); expect(resp).toEqual([[{ foo: true }]]); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.startChild).toBeCalledWith({ + expect(SentryNode.fakeTransaction.startChild).toBeCalledWith({ op: 'http.client.bigquery', description: 'POST /jobs', }); // @ts-ignore see "Why @ts-ignore" note - expect(Sentry.fakeTransaction.startChild).toBeCalledWith({ + expect(SentryNode.fakeTransaction.startChild).toBeCalledWith({ op: 'http.client.bigquery', description: expect.stringMatching(new RegExp('^GET /queries/.+')), }); From c25dece3b90f6beeac6c4e899636e1ea9cb25c49 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Thu, 16 Mar 2023 08:22:11 -0400 Subject: [PATCH 23/54] test(browser-integration): Skip "browsertracing/backgroundtab-pageload" test for firefox (#7475) --- .../backgroundtab-pageload/test.ts | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts index f4ccf67b6c14..af7de77b93f7 100644 --- a/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts +++ b/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts @@ -4,17 +4,24 @@ import type { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; -sentryTest('should finish pageload transaction when the page goes background', async ({ getLocalTestPath, page }) => { - const url = await getLocalTestPath({ testDir: __dirname }); +sentryTest( + 'should finish pageload transaction when the page goes background', + async ({ browserName, getLocalTestPath, page }) => { + // TODO: This is flakey on firefox... trace.status is sometimes undefined + if (['firefox'].includes(browserName)) { + sentryTest.skip(); + } + const url = await getLocalTestPath({ testDir: __dirname }); - await page.goto(url); - await page.click('#go-background'); + await page.goto(url); + await page.click('#go-background'); - const pageloadTransaction = await getFirstSentryEnvelopeRequest(page); + const pageloadTransaction = await getFirstSentryEnvelopeRequest(page); - expect(pageloadTransaction.contexts?.trace?.op).toBe('pageload'); - expect(pageloadTransaction.contexts?.trace?.status).toBe('cancelled'); - expect(pageloadTransaction.contexts?.trace?.tags).toMatchObject({ - visibilitychange: 'document.hidden', - }); -}); + expect(pageloadTransaction.contexts?.trace?.op).toBe('pageload'); + expect(pageloadTransaction.contexts?.trace?.status).toBe('cancelled'); + expect(pageloadTransaction.contexts?.trace?.tags).toMatchObject({ + visibilitychange: 'document.hidden', + }); + }, +); From 7399b9980132b604dfe55cd16cf3a646676ebef9 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 16 Mar 2023 13:28:52 +0100 Subject: [PATCH 24/54] chore(repo): Fix `yarn clean(:all)` scripts (#7478) --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3e720b7a5af7..4b9b23b531ce 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,11 @@ "build:types:watch": "ts-node scripts/build-types-watch.ts", "build:tarball": "lerna run build:tarball", "circularDepCheck": "lerna run circularDepCheck", - "clean": "run-p clean:build clean:caches", + "clean": "run-s clean:build clean:caches", "clean:build": "lerna run clean", "clean:caches": "yarn rimraf eslintcache .nxcache && yarn jest --clearCache", "clean:deps": "lerna clean --yes && rm -rf node_modules && yarn", - "clean:all": "run-p clean:build clean:caches clean:deps", + "clean:all": "run-s clean:build clean:caches clean:deps", "codecov": "codecov", "fix": "lerna run fix", "changelog": "ts-node ./scripts/get-commit-list.ts", From 3161576dd5757dc2590ffaff9700743be33e5112 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 16 Mar 2023 13:36:06 +0100 Subject: [PATCH 25/54] fix(sveltekit): Remove package.json exports (#7481) --- packages/sveltekit/package.json | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index b1248af13f91..d9f9da77528d 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -13,18 +13,6 @@ "module": "build/esm/index.server.js", "browser": "build/esm/index.client.js", "types": "build/types/index.types.d.ts", - "exports": { - "browser": { - "import": "./build/esm/index.client.js", - "require": "./build/cjs/index.client.js", - "default": "./build/esm/index.client.js" - }, - "node": { - "import": "./build/esm/index.server.js", - "require": "./build/cjs/index.server.js", - "default": "./build/esm/index.server.js" - } - }, "publishConfig": { "access": "public" }, From 59e0a87eadc647f8dcd530d2dc982a3c909cbd89 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 16 Mar 2023 14:01:21 +0100 Subject: [PATCH 26/54] feat(serverless): Publish lambda layer for Node 16/18 (#7483) --- .craft.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.craft.yml b/.craft.yml index c21726412f60..dda9ddcda82a 100644 --- a/.craft.yml +++ b/.craft.yml @@ -11,6 +11,8 @@ targets: - nodejs10.x - nodejs12.x - nodejs14.x + - nodejs16.x + - nodejs18.x license: MIT - name: gcs includeNames: /.*\.js.*$/ From 7aa20d04a3d61f30600ed6367ca7151d183a8fc9 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 16 Mar 2023 14:19:04 +0100 Subject: [PATCH 27/54] feat(replay): Use new afterSend hook to improve error linking (#7390) --------- Co-authored-by: Lukas Stracke --- .../suites/replay/errors/droppedError/test.ts | 55 +-- .../errors/errorModeCustomTransport/init.js | 22 ++ .../errors/errorModeCustomTransport/test.ts | 42 +++ .../suites/replay/errors/errorNotSent/init.js | 15 + .../suites/replay/errors/errorNotSent/test.ts | 41 +++ .../utils/replayHelpers.ts | 13 +- packages/browser/src/client.ts | 2 +- packages/core/src/baseclient.ts | 21 +- packages/core/src/transports/base.ts | 4 + packages/core/test/lib/base.test.ts | 130 +++++++ .../src/coreHandlers/handleAfterSendEvent.ts | 88 +++++ .../src/coreHandlers/handleGlobalEvent.ts | 60 ++-- packages/replay/src/replay.ts | 6 - .../replay/src/util/addGlobalListeners.ts | 14 +- packages/replay/src/util/eventUtils.ts | 16 + .../src/util/monkeyPatchRecordDroppedEvent.ts | 45 --- packages/replay/test/fixtures/transaction.ts | 4 +- .../coreHandlers/handleAfterSendEvent.test.ts | 331 ++++++++++++++++++ .../coreHandlers/handleGlobalEvent.test.ts | 126 ++++--- .../test/integration/errorSampleRate.test.ts | 22 +- packages/replay/test/mocks/mockSdk.ts | 20 +- packages/types/src/client.ts | 16 +- 22 files changed, 899 insertions(+), 194 deletions(-) create mode 100644 packages/browser-integration-tests/suites/replay/errors/errorModeCustomTransport/init.js create mode 100644 packages/browser-integration-tests/suites/replay/errors/errorModeCustomTransport/test.ts create mode 100644 packages/browser-integration-tests/suites/replay/errors/errorNotSent/init.js create mode 100644 packages/browser-integration-tests/suites/replay/errors/errorNotSent/test.ts create mode 100644 packages/replay/src/coreHandlers/handleAfterSendEvent.ts create mode 100644 packages/replay/src/util/eventUtils.ts delete mode 100644 packages/replay/src/util/monkeyPatchRecordDroppedEvent.ts create mode 100644 packages/replay/test/integration/coreHandlers/handleAfterSendEvent.test.ts diff --git a/packages/browser-integration-tests/suites/replay/errors/droppedError/test.ts b/packages/browser-integration-tests/suites/replay/errors/droppedError/test.ts index 5bcb75bfe619..b63ca9a8b61f 100644 --- a/packages/browser-integration-tests/suites/replay/errors/droppedError/test.ts +++ b/packages/browser-integration-tests/suites/replay/errors/droppedError/test.ts @@ -1,37 +1,25 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { getExpectedReplayEvent } from '../../../../utils/replayEventTemplates'; -import { getReplayEvent, shouldSkipReplayTest, waitForReplayRequest } from '../../../../utils/replayHelpers'; +import { envelopeRequestParser } from '../../../../utils/helpers'; +import { getReplaySnapshot, isReplayEvent, shouldSkipReplayTest } from '../../../../utils/replayHelpers'; -/* - * This scenario currently shows somewhat unexpected behavior from the PoV of a user: - * The error is dropped, but the recording is started and continued anyway. - * If folks only sample error replays, this will lead to a lot of confusion as the resulting replay - * won't contain the error that started it (possibly none or only additional errors that occurred later on). - * - * This is because in error-mode, we start recording as soon as replay's eventProcessor is called with an error. - * If later event processors or beforeSend drop the error, the recording is already started. - * - * We'll need a proper SDK lifecycle hook (WIP) to fix this properly. - * TODO: Once we have lifecycle hooks, we should revisit this test and make sure it behaves as expected. - * This means that the recording should not be started or stopped if the error that triggered it is not sent. - */ sentryTest( - '[error-mode] should start recording if an error occurred although the error was dropped', - async ({ getLocalTestPath, page }) => { + '[error-mode] should not start recording if an error occurred when the error was dropped', + async ({ getLocalTestPath, page, forceFlushReplay }) => { if (shouldSkipReplayTest()) { sentryTest.skip(); } - const reqPromise0 = waitForReplayRequest(page, 0); - const reqPromise1 = waitForReplayRequest(page, 1); - const reqPromise2 = waitForReplayRequest(page, 2); - let callsToSentry = 0; await page.route('https://dsn.ingest.sentry.io/**/*', route => { - callsToSentry++; + const req = route.request(); + const event = envelopeRequestParser(req); + + if (isReplayEvent(event)) { + callsToSentry++; + } return route.fulfill({ status: 200, @@ -43,30 +31,17 @@ sentryTest( const url = await getLocalTestPath({ testDir: __dirname }); await page.goto(url); - await page.click('#go-background'); + await forceFlushReplay(); expect(callsToSentry).toEqual(0); await page.click('#error'); - const req0 = await reqPromise0; - - await page.click('#go-background'); - await reqPromise1; await page.click('#log'); - await page.click('#go-background'); - await reqPromise2; + await forceFlushReplay(); - // Note: The fact that reqPromise1/reqPromise2 are fulfilled prooves that the recording continues - - const event0 = getReplayEvent(req0); + expect(callsToSentry).toEqual(0); - expect(event0).toEqual( - getExpectedReplayEvent({ - contexts: { replay: { error_sample_rate: 1, session_sample_rate: 0 } }, - // This is by design. A dropped error shouldn't be in this list. - error_ids: [], - replay_type: 'error', - }), - ); + const replay = await getReplaySnapshot(page); + expect(replay.recordingMode).toBe('error'); }, ); diff --git a/packages/browser-integration-tests/suites/replay/errors/errorModeCustomTransport/init.js b/packages/browser-integration-tests/suites/replay/errors/errorModeCustomTransport/init.js new file mode 100644 index 000000000000..dca771f16c87 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/errors/errorModeCustomTransport/init.js @@ -0,0 +1,22 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window.Replay = new Sentry.Replay({ + flushMinDelay: 500, + flushMaxDelay: 500, +}); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 1, + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + integrations: [window.Replay], + transport: options => { + const transport = new Sentry.makeXHRTransport(options); + + delete transport.send.__sentry__baseTransport__; + + return transport; + }, +}); diff --git a/packages/browser-integration-tests/suites/replay/errors/errorModeCustomTransport/test.ts b/packages/browser-integration-tests/suites/replay/errors/errorModeCustomTransport/test.ts new file mode 100644 index 000000000000..8efe303afb88 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/errors/errorModeCustomTransport/test.ts @@ -0,0 +1,42 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../utils/fixtures'; +import { getReplaySnapshot, shouldSkipReplayTest, waitForReplayRequest } from '../../../../utils/replayHelpers'; + +sentryTest( + '[error-mode] should handle errors with custom transport', + async ({ getLocalTestPath, page, forceFlushReplay }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + const promiseReq0 = waitForReplayRequest(page, 0); + const promiseReq1 = waitForReplayRequest(page, 1); + + let callsToSentry = 0; + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + callsToSentry++; + + return route.fulfill({ + // Only error out for error, then succeed + status: callsToSentry === 1 ? 422 : 200, + }); + }); + + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.goto(url); + await forceFlushReplay(); + expect(callsToSentry).toEqual(0); + + await page.click('#error'); + await promiseReq0; + + await forceFlushReplay(); + await promiseReq1; + + const replay = await getReplaySnapshot(page); + expect(replay.recordingMode).toBe('session'); + }, +); diff --git a/packages/browser-integration-tests/suites/replay/errors/errorNotSent/init.js b/packages/browser-integration-tests/suites/replay/errors/errorNotSent/init.js new file mode 100644 index 000000000000..4f8dcac1ea28 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/errors/errorNotSent/init.js @@ -0,0 +1,15 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window.Replay = new Sentry.Replay({ + flushMinDelay: 500, + flushMaxDelay: 500, +}); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 1, + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + integrations: [window.Replay], +}); diff --git a/packages/browser-integration-tests/suites/replay/errors/errorNotSent/test.ts b/packages/browser-integration-tests/suites/replay/errors/errorNotSent/test.ts new file mode 100644 index 000000000000..963f47e9919d --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/errors/errorNotSent/test.ts @@ -0,0 +1,41 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../utils/fixtures'; +import { getReplaySnapshot, shouldSkipReplayTest } from '../../../../utils/replayHelpers'; + +sentryTest( + '[error-mode] should handle errors that result in API error response', + async ({ getLocalTestPath, page, forceFlushReplay }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + let callsToSentry = 0; + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + callsToSentry++; + + return route.fulfill({ + status: 422, + contentType: 'application/json', + }); + }); + + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.goto(url); + await forceFlushReplay(); + expect(callsToSentry).toEqual(0); + + await page.click('#error'); + + await page.click('#log'); + await forceFlushReplay(); + + // Only sent once, but since API failed we do not go into session mode + expect(callsToSentry).toEqual(1); + + const replay = await getReplaySnapshot(page); + expect(replay.recordingMode).toBe('error'); + }, +); diff --git a/packages/browser-integration-tests/utils/replayHelpers.ts b/packages/browser-integration-tests/utils/replayHelpers.ts index 8722fe245a23..cf21ce7b9c7b 100644 --- a/packages/browser-integration-tests/utils/replayHelpers.ts +++ b/packages/browser-integration-tests/utils/replayHelpers.ts @@ -7,7 +7,7 @@ import type { Session, } from '@sentry/replay/build/npm/types/types'; import type { eventWithTime } from '@sentry/replay/build/npm/types/types/rrweb'; -import type { Breadcrumb, Event, ReplayEvent } from '@sentry/types'; +import type { Breadcrumb, Event, ReplayEvent, ReplayRecordingMode } from '@sentry/types'; import pako from 'pako'; import type { Page, Request, Response } from 'playwright'; @@ -104,9 +104,13 @@ function isCustomSnapshot(event: RecordingEvent): event is RecordingEvent & { da * Note that due to how this works with playwright, this is a POJO copy of replay. * This means that we cannot access any methods on it, and also not mutate it in any way. */ -export async function getReplaySnapshot( - page: Page, -): Promise<{ _isPaused: boolean; _isEnabled: boolean; _context: InternalEventContext; session: Session | undefined }> { +export async function getReplaySnapshot(page: Page): Promise<{ + _isPaused: boolean; + _isEnabled: boolean; + _context: InternalEventContext; + session: Session | undefined; + recordingMode: ReplayRecordingMode; +}> { return await page.evaluate(() => { const replayIntegration = (window as unknown as Window & { Replay: { _replay: ReplayContainer } }).Replay; const replay = replayIntegration._replay; @@ -116,6 +120,7 @@ export async function getReplaySnapshot( _isEnabled: replay.isEnabled(), _context: replay.getContext(), session: replay.session, + recordingMode: replay.recordingMode, }; return replaySnapshot; diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index e603cba661dd..8cafc77a68c3 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -146,7 +146,7 @@ export class BrowserClient extends BaseClient { } else { // If beacon is not supported or if they are using the tunnel option // use our regular transport to send client reports to Sentry. - this._sendEnvelope(envelope); + void this._sendEnvelope(envelope); } } catch (e) { __DEBUG_BUILD__ && logger.error(e); diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index bf1ae616c4d6..80ed36450ef5 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -20,6 +20,7 @@ import type { Transaction, TransactionEvent, Transport, + TransportMakeRequestResponse, } from '@sentry/types'; import { addItemToEnvelope, @@ -320,7 +321,10 @@ export abstract class BaseClient implements Client { ); } - this._sendEnvelope(env); + const promise = this._sendEnvelope(env); + if (promise) { + promise.then(sendResponse => this.emit('afterSendEvent', event, sendResponse), null); + } } } @@ -330,7 +334,7 @@ export abstract class BaseClient implements Client { public sendSession(session: Session | SessionAggregates): void { if (this._dsn) { const env = createSessionEnvelope(session, this._dsn, this._options._metadata, this._options.tunnel); - this._sendEnvelope(env); + void this._sendEnvelope(env); } } @@ -363,6 +367,12 @@ export abstract class BaseClient implements Client { /** @inheritdoc */ public on(hook: 'beforeEnvelope', callback: (envelope: Envelope) => void): void; + /** @inheritdoc */ + public on( + hook: 'afterSendEvent', + callback: (event: Event, sendResponse: TransportMakeRequestResponse | void) => void, + ): void; + /** @inheritdoc */ public on(hook: string, callback: unknown): void { if (!this._hooks[hook]) { @@ -379,6 +389,9 @@ export abstract class BaseClient implements Client { /** @inheritdoc */ public emit(hook: 'beforeEnvelope', envelope: Envelope): void; + /** @inheritdoc */ + public emit(hook: 'afterSendEvent', event: Event, sendResponse: TransportMakeRequestResponse | void): void; + /** @inheritdoc */ public emit(hook: string, ...rest: unknown[]): void { if (this._hooks[hook]) { @@ -624,9 +637,9 @@ export abstract class BaseClient implements Client { /** * @inheritdoc */ - protected _sendEnvelope(envelope: Envelope): void { + protected _sendEnvelope(envelope: Envelope): PromiseLike | void { if (this._transport && this._dsn) { - this._transport.send(envelope).then(null, reason => { + return this._transport.send(envelope).then(null, reason => { __DEBUG_BUILD__ && logger.error('Error while sending event:', reason); }); } else { diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 19a92ebc8599..5b11db7e9c17 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -109,6 +109,10 @@ export function createTransport( ); } + // We use this to identifify if the transport is the base transport + // TODO (v8): Remove this again as we'll no longer need it + send.__sentry__baseTransport__ = true; + return { send, flush, diff --git a/packages/core/test/lib/base.test.ts b/packages/core/test/lib/base.test.ts index 4ee65114fe79..9705f7a6622f 100644 --- a/packages/core/test/lib/base.test.ts +++ b/packages/core/test/lib/base.test.ts @@ -1634,6 +1634,136 @@ describe('BaseClient', () => { }); }); + describe('sendEvent', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('emits `afterSendEvent` when sending an error', async () => { + const client = new TestClient( + getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + enableSend: true, + }), + ); + + // @ts-ignore + const mockSend = jest.spyOn(client._transport, 'send'); + + const errorEvent: Event = { message: 'error' }; + + const callback = jest.fn(); + client.on('afterSendEvent', callback); + + client.sendEvent(errorEvent); + jest.runAllTimers(); + // Wait for two ticks + // note that for whatever reason, await new Promise(resolve => setTimeout(resolve, 0)) causes the test to hang + await undefined; + await undefined; + + expect(mockSend).toBeCalledTimes(1); + expect(callback).toBeCalledTimes(1); + expect(callback).toBeCalledWith(errorEvent, {}); + }); + + it('emits `afterSendEvent` when sending a transaction', async () => { + const client = new TestClient( + getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + enableSend: true, + }), + ); + + // @ts-ignore + const mockSend = jest.spyOn(client._transport, 'send'); + + const transactionEvent: Event = { type: 'transaction', event_id: 'tr1' }; + + const callback = jest.fn(); + client.on('afterSendEvent', callback); + + client.sendEvent(transactionEvent); + jest.runAllTimers(); + // Wait for two ticks + // note that for whatever reason, await new Promise(resolve => setTimeout(resolve, 0)) causes the test to hang + await undefined; + await undefined; + + expect(mockSend).toBeCalledTimes(1); + expect(callback).toBeCalledTimes(1); + expect(callback).toBeCalledWith(transactionEvent, {}); + }); + + it('still triggers `afterSendEvent` when transport.send rejects', async () => { + expect.assertions(3); + + const client = new TestClient( + getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + enableSend: true, + }), + ); + + // @ts-ignore + const mockSend = jest.spyOn(client._transport, 'send').mockImplementation(() => { + return Promise.reject('send error'); + }); + + const errorEvent: Event = { message: 'error' }; + + const callback = jest.fn(); + client.on('afterSendEvent', callback); + + client.sendEvent(errorEvent); + jest.runAllTimers(); + // Wait for two ticks + // note that for whatever reason, await new Promise(resolve => setTimeout(resolve, 0)) causes the test to hang + await undefined; + await undefined; + + expect(mockSend).toBeCalledTimes(1); + expect(callback).toBeCalledTimes(1); + expect(callback).toBeCalledWith(errorEvent, undefined); + }); + + it('passes the response to the hook', async () => { + expect.assertions(3); + + const client = new TestClient( + getDefaultTestClientOptions({ + dsn: PUBLIC_DSN, + enableSend: true, + }), + ); + + // @ts-ignore + const mockSend = jest.spyOn(client._transport, 'send').mockImplementation(() => { + return Promise.resolve({ statusCode: 200 }); + }); + + const errorEvent: Event = { message: 'error' }; + + const callback = jest.fn(); + client.on('afterSendEvent', callback); + + client.sendEvent(errorEvent); + jest.runAllTimers(); + // Wait for two ticks + // note that for whatever reason, await new Promise(resolve => setTimeout(resolve, 0)) causes the test to hang + await undefined; + await undefined; + + expect(mockSend).toBeCalledTimes(1); + expect(callback).toBeCalledTimes(1); + expect(callback).toBeCalledWith(errorEvent, { statusCode: 200 }); + }); + }); + describe('captureSession()', () => { test('sends sessions to the client', () => { expect.assertions(1); diff --git a/packages/replay/src/coreHandlers/handleAfterSendEvent.ts b/packages/replay/src/coreHandlers/handleAfterSendEvent.ts new file mode 100644 index 000000000000..f3a531f3ffdc --- /dev/null +++ b/packages/replay/src/coreHandlers/handleAfterSendEvent.ts @@ -0,0 +1,88 @@ +import { getCurrentHub } from '@sentry/core'; +import type { Event, Transport, TransportMakeRequestResponse } from '@sentry/types'; + +import { UNABLE_TO_SEND_REPLAY } from '../constants'; +import type { ReplayContainer } from '../types'; +import { isErrorEvent, isTransactionEvent } from '../util/eventUtils'; + +type AfterSendEventCallback = (event: Event, sendResponse: TransportMakeRequestResponse | void) => void; + +/** + * Returns a listener to be added to `client.on('afterSendErrorEvent, listener)`. + */ +export function handleAfterSendEvent(replay: ReplayContainer): AfterSendEventCallback { + // Custom transports may still be returning `Promise`, which means we cannot expect the status code to be available there + // TODO (v8): remove this check as it will no longer be necessary + const enforceStatusCode = isBaseTransportSend(); + + return (event: Event, sendResponse: TransportMakeRequestResponse | void) => { + if (!isErrorEvent(event) && !isTransactionEvent(event)) { + return; + } + + const statusCode = sendResponse && sendResponse.statusCode; + + // We only want to do stuff on successful error sending, otherwise you get error replays without errors attached + // If not using the base transport, we allow `undefined` response (as a custom transport may not implement this correctly yet) + // If we do use the base transport, we skip if we encountered an non-OK status code + if (enforceStatusCode && (!statusCode || statusCode < 200 || statusCode >= 300)) { + return; + } + + // Collect traceIds in _context regardless of `recordingMode` + // In error mode, _context gets cleared on every checkout + if (isTransactionEvent(event) && event.contexts && event.contexts.trace && event.contexts.trace.trace_id) { + replay.getContext().traceIds.add(event.contexts.trace.trace_id as string); + return; + } + + // Everything below is just for error events + if (!isErrorEvent(event)) { + return; + } + + // Add error to list of errorIds of replay + if (event.event_id) { + replay.getContext().errorIds.add(event.event_id); + } + + // Trigger error recording + // Need to be very careful that this does not cause an infinite loop + if ( + replay.recordingMode === 'error' && + event.exception && + event.message !== UNABLE_TO_SEND_REPLAY // ignore this error because otherwise we could loop indefinitely with trying to capture replay and failing + ) { + setTimeout(async () => { + // Allow flush to complete before resuming as a session recording, otherwise + // the checkout from `startRecording` may be included in the payload. + // Prefer to keep the error replay as a separate (and smaller) segment + // than the session replay. + await replay.flushImmediate(); + + if (replay.stopRecording()) { + // Reset all "capture on error" configuration before + // starting a new recording + replay.recordingMode = 'session'; + replay.startRecording(); + } + }); + } + }; +} + +function isBaseTransportSend(): boolean { + const client = getCurrentHub().getClient(); + if (!client) { + return false; + } + + const transport = client.getTransport(); + if (!transport) { + return false; + } + + return ( + (transport.send as Transport['send'] & { __sentry__baseTransport__?: true }).__sentry__baseTransport__ || false + ); +} diff --git a/packages/replay/src/coreHandlers/handleGlobalEvent.ts b/packages/replay/src/coreHandlers/handleGlobalEvent.ts index 35ecba8c43e3..ba539b661387 100644 --- a/packages/replay/src/coreHandlers/handleGlobalEvent.ts +++ b/packages/replay/src/coreHandlers/handleGlobalEvent.ts @@ -2,23 +2,33 @@ import { addBreadcrumb } from '@sentry/core'; import type { Event, EventHint } from '@sentry/types'; import { logger } from '@sentry/utils'; -import { REPLAY_EVENT_NAME, UNABLE_TO_SEND_REPLAY } from '../constants'; import type { ReplayContainer } from '../types'; +import { isErrorEvent, isReplayEvent, isTransactionEvent } from '../util/eventUtils'; import { isRrwebError } from '../util/isRrwebError'; +import { handleAfterSendEvent } from './handleAfterSendEvent'; /** * Returns a listener to be added to `addGlobalEventProcessor(listener)`. */ -export function handleGlobalEventListener(replay: ReplayContainer): (event: Event, hint: EventHint) => Event | null { +export function handleGlobalEventListener( + replay: ReplayContainer, + includeAfterSendEventHandling = false, +): (event: Event, hint: EventHint) => Event | null { + const afterSendHandler = includeAfterSendEventHandling ? handleAfterSendEvent(replay) : undefined; + return (event: Event, hint: EventHint) => { - // Do not apply replayId to the root event - if (event.type === REPLAY_EVENT_NAME) { + if (isReplayEvent(event)) { // Replays have separate set of breadcrumbs, do not include breadcrumbs // from core SDK delete event.breadcrumbs; return event; } + // We only want to handle errors & transactions, nothing else + if (!isErrorEvent(event) && !isTransactionEvent(event)) { + return event; + } + // Unless `captureExceptions` is enabled, we want to ignore errors coming from rrweb // As there can be a bunch of stuff going wrong in internals there, that we don't want to bubble up to users if (isRrwebError(event, hint) && !replay.getOptions()._experiments.captureExceptions) { @@ -27,50 +37,22 @@ export function handleGlobalEventListener(replay: ReplayContainer): (event: Even } // Only tag transactions with replayId if not waiting for an error - // @ts-ignore private - if (!event.type || replay.recordingMode === 'session') { + if (isErrorEvent(event) || (isTransactionEvent(event) && replay.recordingMode === 'session')) { event.tags = { ...event.tags, replayId: replay.getSessionId() }; } - // Collect traceIds in _context regardless of `recordingMode` - if it's true, - // _context gets cleared on every checkout - if (event.type === 'transaction' && event.contexts && event.contexts.trace && event.contexts.trace.trace_id) { - replay.getContext().traceIds.add(event.contexts.trace.trace_id as string); - return event; - } - - // no event type means error - if (!event.type) { - replay.getContext().errorIds.add(event.event_id as string); - } - - if (__DEBUG_BUILD__ && replay.getOptions()._experiments.traceInternals) { + if (__DEBUG_BUILD__ && replay.getOptions()._experiments.traceInternals && isErrorEvent(event)) { const exc = getEventExceptionValues(event); addInternalBreadcrumb({ message: `Tagging event (${event.event_id}) - ${event.message} - ${exc.type}: ${exc.value}`, }); } - // Need to be very careful that this does not cause an infinite loop - if ( - replay.recordingMode === 'error' && - event.exception && - event.message !== UNABLE_TO_SEND_REPLAY // ignore this error because otherwise we could loop indefinitely with trying to capture replay and failing - ) { - setTimeout(async () => { - // Allow flush to complete before resuming as a session recording, otherwise - // the checkout from `startRecording` may be included in the payload. - // Prefer to keep the error replay as a separate (and smaller) segment - // than the session replay. - await replay.flushImmediate(); - - if (replay.stopRecording()) { - // Reset all "capture on error" configuration before - // starting a new recording - replay.recordingMode = 'session'; - replay.startRecording(); - } - }); + // In cases where a custom client is used that does not support the new hooks (yet), + // we manually call this hook method here + if (afterSendHandler) { + // Pretend the error had a 200 response so we always capture it + afterSendHandler(event, { statusCode: 200 }); } return event; diff --git a/packages/replay/src/replay.ts b/packages/replay/src/replay.ts index 7f397855d17f..c42a5cc5b9b6 100644 --- a/packages/replay/src/replay.ts +++ b/packages/replay/src/replay.ts @@ -32,7 +32,6 @@ import { debounce } from './util/debounce'; import { getHandleRecordingEmit } from './util/handleRecordingEmit'; import { isExpired } from './util/isExpired'; import { isSessionExpired } from './util/isSessionExpired'; -import { overwriteRecordDroppedEvent, restoreRecordDroppedEvent } from './util/monkeyPatchRecordDroppedEvent'; import { sendReplay } from './util/sendReplay'; /** @@ -499,9 +498,6 @@ export class ReplayContainer implements ReplayContainerInterface { WINDOW.addEventListener('blur', this._handleWindowBlur); WINDOW.addEventListener('focus', this._handleWindowFocus); - // We need to filter out dropped events captured by `addGlobalEventProcessor(this.handleGlobalEvent)` below - overwriteRecordDroppedEvent(this._context.errorIds); - // There is no way to remove these listeners, so ensure they are only added once if (!this._hasInitializedCoreListeners) { addGlobalListeners(this); @@ -530,8 +526,6 @@ export class ReplayContainer implements ReplayContainerInterface { WINDOW.removeEventListener('blur', this._handleWindowBlur); WINDOW.removeEventListener('focus', this._handleWindowFocus); - restoreRecordDroppedEvent(); - if (this._performanceObserver) { this._performanceObserver.disconnect(); this._performanceObserver = null; diff --git a/packages/replay/src/util/addGlobalListeners.ts b/packages/replay/src/util/addGlobalListeners.ts index dfef03b094d4..94146962911f 100644 --- a/packages/replay/src/util/addGlobalListeners.ts +++ b/packages/replay/src/util/addGlobalListeners.ts @@ -1,6 +1,8 @@ +import type { BaseClient } from '@sentry/core'; import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; import { addInstrumentationHandler } from '@sentry/utils'; +import { handleAfterSendEvent } from '../coreHandlers/handleAfterSendEvent'; import { handleDomListener } from '../coreHandlers/handleDom'; import { handleFetchSpanListener } from '../coreHandlers/handleFetch'; import { handleGlobalEventListener } from '../coreHandlers/handleGlobalEvent'; @@ -15,6 +17,8 @@ import type { ReplayContainer } from '../types'; export function addGlobalListeners(replay: ReplayContainer): void { // Listeners from core SDK // const scope = getCurrentHub().getScope(); + const client = getCurrentHub().getClient(); + if (scope) { scope.addScopeListener(handleScopeListener(replay)); } @@ -23,7 +27,15 @@ export function addGlobalListeners(replay: ReplayContainer): void { addInstrumentationHandler('xhr', handleXhrSpanListener(replay)); addInstrumentationHandler('history', handleHistorySpanListener(replay)); + // If a custom client has no hooks yet, we continue to use the "old" implementation + const hasHooks = !!(client && client.on); + // Tag all (non replay) events that get sent to Sentry with the current // replay ID so that we can reference them later in the UI - addGlobalEventProcessor(handleGlobalEventListener(replay)); + addGlobalEventProcessor(handleGlobalEventListener(replay, !hasHooks)); + + if (hasHooks) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (client as BaseClient).on('afterSendEvent', handleAfterSendEvent(replay)); + } } diff --git a/packages/replay/src/util/eventUtils.ts b/packages/replay/src/util/eventUtils.ts new file mode 100644 index 000000000000..5cd4989a8267 --- /dev/null +++ b/packages/replay/src/util/eventUtils.ts @@ -0,0 +1,16 @@ +import type { ErrorEvent, Event, ReplayEvent, TransactionEvent } from '@sentry/types'; + +/** If the event is an error event */ +export function isErrorEvent(event: Event): event is ErrorEvent { + return !event.type; +} + +/** If the event is a transaction event */ +export function isTransactionEvent(event: Event): event is TransactionEvent { + return event.type === 'transaction'; +} + +/** If the event is an replay event */ +export function isReplayEvent(event: Event): event is ReplayEvent { + return event.type === 'replay_event'; +} diff --git a/packages/replay/src/util/monkeyPatchRecordDroppedEvent.ts b/packages/replay/src/util/monkeyPatchRecordDroppedEvent.ts deleted file mode 100644 index 6a819ef917b2..000000000000 --- a/packages/replay/src/util/monkeyPatchRecordDroppedEvent.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { getCurrentHub } from '@sentry/core'; -import type { Client, DataCategory, Event, EventDropReason } from '@sentry/types'; - -let _originalRecordDroppedEvent: Client['recordDroppedEvent'] | undefined; - -/** - * Overwrite the `recordDroppedEvent` method on the client, so we can find out which events were dropped. - * */ -export function overwriteRecordDroppedEvent(errorIds: Set): void { - const client = getCurrentHub().getClient(); - - if (!client) { - return; - } - - const _originalCallback = client.recordDroppedEvent.bind(client); - - const recordDroppedEvent: Client['recordDroppedEvent'] = ( - reason: EventDropReason, - category: DataCategory, - event?: Event, - ): void => { - if (event && !event.type && event.event_id) { - errorIds.delete(event.event_id); - } - - return _originalCallback(reason, category, event); - }; - - client.recordDroppedEvent = recordDroppedEvent; - _originalRecordDroppedEvent = _originalCallback; -} - -/** - * Restore the original method. - * */ -export function restoreRecordDroppedEvent(): void { - const client = getCurrentHub().getClient(); - - if (!client || !_originalRecordDroppedEvent) { - return; - } - - client.recordDroppedEvent = _originalRecordDroppedEvent; -} diff --git a/packages/replay/test/fixtures/transaction.ts b/packages/replay/test/fixtures/transaction.ts index 2899740c5e52..1e8ec7a3272a 100644 --- a/packages/replay/test/fixtures/transaction.ts +++ b/packages/replay/test/fixtures/transaction.ts @@ -1,6 +1,6 @@ import type { Event, SeverityLevel } from '@sentry/types'; -export function Transaction(obj?: Partial): any { +export function Transaction(traceId?: string, obj?: Partial): any { const timestamp = new Date().getTime() / 1000; return { @@ -22,7 +22,7 @@ export function Transaction(obj?: Partial): any { hardwareConcurrency: '10', sentry_reportAllChanges: false, }, - trace_id: 'trace_id', + trace_id: traceId || 'trace_id', }, }, // }}} spans: [ diff --git a/packages/replay/test/integration/coreHandlers/handleAfterSendEvent.test.ts b/packages/replay/test/integration/coreHandlers/handleAfterSendEvent.test.ts new file mode 100644 index 000000000000..d46bacf4aede --- /dev/null +++ b/packages/replay/test/integration/coreHandlers/handleAfterSendEvent.test.ts @@ -0,0 +1,331 @@ +import { getCurrentHub } from '@sentry/core'; +import type { ErrorEvent, Event } from '@sentry/types'; + +import { UNABLE_TO_SEND_REPLAY } from '../../../src/constants'; +import { handleAfterSendEvent } from '../../../src/coreHandlers/handleAfterSendEvent'; +import type { ReplayContainer } from '../../../src/replay'; +import { Error } from '../../fixtures/error'; +import { Transaction } from '../../fixtures/transaction'; +import { resetSdkMock } from '../../mocks/resetSdkMock'; +import { useFakeTimers } from '../../utils/use-fake-timers'; + +useFakeTimers(); +let replay: ReplayContainer; + +describe('Integration | coreHandlers | handleAfterSendEvent', () => { + afterEach(() => { + replay.stop(); + }); + + it('records errorIds from sent error events', async () => { + ({ replay } = await resetSdkMock({ + replayOptions: { + stickySession: false, + }, + sentryOptions: { + replaysSessionSampleRate: 1.0, + replaysOnErrorSampleRate: 0.0, + }, + })); + + const error1 = Error({ event_id: 'err1' }); + const error2 = Error({ event_id: 'err2' }); + const error3 = Error({ event_id: 'err3' }); + const error4 = Error({ event_id: 'err4' }); + + const handler = handleAfterSendEvent(replay); + + // With undefined response: Don't capture + handler(error1, undefined); + // With "successful" response: Capture + handler(error2, { statusCode: 200 }); + // With "unsuccessful" response: Don't capture + handler(error3, { statusCode: 0 }); + // With no statusCode response: Don't Capture + handler(error4, { statusCode: undefined }); + + expect(Array.from(replay.getContext().errorIds)).toEqual(['err2']); + expect(Array.from(replay.getContext().traceIds)).toEqual([]); + }); + + it('records traceIds from sent transaction events', async () => { + ({ replay } = await resetSdkMock({ + replayOptions: { + stickySession: false, + }, + sentryOptions: { + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + }, + })); + + const transaction1 = Transaction('tr1'); + const transaction2 = Transaction('tr2'); + const transaction3 = Transaction('tr3'); + const transaction4 = Transaction('tr4'); + + const handler = handleAfterSendEvent(replay); + + // With undefined response: Don't capture + handler(transaction1, undefined); + // With "successful" response: Capture + handler(transaction2, { statusCode: 200 }); + // With "unsuccessful" response: Don't capture + handler(transaction3, { statusCode: 0 }); + // With no statusCode response: Don't Capture + handler(transaction4, { statusCode: undefined }); + + expect(Array.from(replay.getContext().errorIds)).toEqual([]); + expect(Array.from(replay.getContext().traceIds)).toEqual(['tr2']); + + // Does not affect error session + jest.runAllTimers(); + await new Promise(process.nextTick); + + expect(Array.from(replay.getContext().errorIds)).toEqual([]); + expect(Array.from(replay.getContext().traceIds)).toEqual(['tr2']); + expect(replay.isEnabled()).toBe(true); + expect(replay.isPaused()).toBe(false); + expect(replay.recordingMode).toBe('error'); + }); + + it('allows undefined send response when using custom transport', async () => { + ({ replay } = await resetSdkMock({ + replayOptions: { + stickySession: false, + }, + sentryOptions: { + replaysSessionSampleRate: 1.0, + replaysOnErrorSampleRate: 0.0, + }, + })); + + const client = getCurrentHub().getClient()!; + // @ts-ignore make sure to remove this + delete client.getTransport()!.send.__sentry__baseTransport__; + + const error1 = Error({ event_id: 'err1' }); + const error2 = Error({ event_id: 'err2' }); + const error3 = Error({ event_id: 'err3' }); + const error4 = Error({ event_id: 'err4' }); + + const handler = handleAfterSendEvent(replay); + + // With undefined response: Capture + handler(error1, undefined); + // With "successful" response: Capture + handler(error2, { statusCode: 200 }); + // With "unsuccessful" response: Capture + handler(error3, { statusCode: 0 }); + // With no statusCode response: Capture + handler(error4, { statusCode: undefined }); + + expect(Array.from(replay.getContext().errorIds)).toEqual(['err1', 'err2', 'err3', 'err4']); + }); + + it('flushes when in error mode', async () => { + ({ replay } = await resetSdkMock({ + replayOptions: { + stickySession: false, + }, + sentryOptions: { + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + }, + })); + + const mockSend = getCurrentHub().getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + + const error1 = Error({ event_id: 'err1' }); + + const handler = handleAfterSendEvent(replay); + + expect(replay.recordingMode).toBe('error'); + + handler(error1, { statusCode: 200 }); + + expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); + + jest.runAllTimers(); + await new Promise(process.nextTick); + + // Send twice, one for the error & one right after for the session conversion + expect(mockSend).toHaveBeenCalledTimes(2); + // This is removed now, because it has been converted to a "session" session + expect(Array.from(replay.getContext().errorIds)).toEqual([]); + expect(replay.isEnabled()).toBe(true); + expect(replay.isPaused()).toBe(false); + expect(replay.recordingMode).toBe('session'); + }); + + it('does not flush when in session mode', async () => { + ({ replay } = await resetSdkMock({ + replayOptions: { + stickySession: false, + }, + sentryOptions: { + replaysSessionSampleRate: 1.0, + replaysOnErrorSampleRate: 0.0, + }, + })); + + const mockSend = getCurrentHub().getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + + const error1 = Error({ event_id: 'err1' }); + + const handler = handleAfterSendEvent(replay); + + expect(replay.recordingMode).toBe('session'); + + handler(error1, { statusCode: 200 }); + + expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); + + jest.runAllTimers(); + await new Promise(process.nextTick); + + // Send once for the regular session sending + expect(mockSend).toHaveBeenCalledTimes(1); + expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); + expect(replay.isEnabled()).toBe(true); + expect(replay.isPaused()).toBe(false); + expect(replay.recordingMode).toBe('session'); + }); + + it('ignores profile & replay events', async () => { + ({ replay } = await resetSdkMock({ + replayOptions: { + stickySession: false, + }, + sentryOptions: { + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + }, + })); + + const mockSend = getCurrentHub().getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + + const profileEvent: Event = { type: 'profile' }; + const replayEvent: Event = { type: 'replay_event' }; + + const handler = handleAfterSendEvent(replay); + + expect(replay.recordingMode).toBe('error'); + + handler(profileEvent, { statusCode: 200 }); + handler(replayEvent, { statusCode: 200 }); + + expect(Array.from(replay.getContext().errorIds)).toEqual([]); + + jest.runAllTimers(); + await new Promise(process.nextTick); + + expect(mockSend).toHaveBeenCalledTimes(0); + expect(Array.from(replay.getContext().errorIds)).toEqual([]); + expect(replay.isEnabled()).toBe(true); + expect(replay.isPaused()).toBe(false); + expect(replay.recordingMode).toBe('error'); + }); + + it('does not flush in error mode when failing to send the error', async () => { + ({ replay } = await resetSdkMock({ + replayOptions: { + stickySession: false, + }, + sentryOptions: { + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + }, + })); + + const mockSend = getCurrentHub().getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + + const error1 = Error({ event_id: 'err1' }); + + const handler = handleAfterSendEvent(replay); + + expect(replay.recordingMode).toBe('error'); + + handler(error1, undefined); + + expect(Array.from(replay.getContext().errorIds)).toEqual([]); + + jest.runAllTimers(); + await new Promise(process.nextTick); + + // Remains in error mode & without flushing + expect(mockSend).toHaveBeenCalledTimes(0); + expect(Array.from(replay.getContext().errorIds)).toEqual([]); + expect(replay.isEnabled()).toBe(true); + expect(replay.isPaused()).toBe(false); + expect(replay.recordingMode).toBe('error'); + }); + + it('does not flush if error event has no exception', async () => { + ({ replay } = await resetSdkMock({ + replayOptions: { + stickySession: false, + }, + sentryOptions: { + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + }, + })); + + const mockSend = getCurrentHub().getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + + const error1: ErrorEvent = { event_id: 'err1', type: undefined }; + + const handler = handleAfterSendEvent(replay); + + expect(replay.recordingMode).toBe('error'); + + handler(error1, { statusCode: 200 }); + + expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); + + jest.runAllTimers(); + await new Promise(process.nextTick); + + // Remains in error mode & without flushing + expect(mockSend).toHaveBeenCalledTimes(0); + expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); + expect(replay.isEnabled()).toBe(true); + expect(replay.isPaused()).toBe(false); + expect(replay.recordingMode).toBe('error'); + }); + + it('does not flush if error is replay send error', async () => { + ({ replay } = await resetSdkMock({ + replayOptions: { + stickySession: false, + }, + sentryOptions: { + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + }, + })); + + const mockSend = getCurrentHub().getClient()!.getTransport()!.send as unknown as jest.SpyInstance; + + const error1 = Error({ event_id: 'err1', message: UNABLE_TO_SEND_REPLAY }); + + const handler = handleAfterSendEvent(replay); + + expect(replay.recordingMode).toBe('error'); + + handler(error1, { statusCode: 200 }); + + expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); + + jest.runAllTimers(); + await new Promise(process.nextTick); + + // Remains in error mode & without flushing + expect(mockSend).toHaveBeenCalledTimes(0); + expect(Array.from(replay.getContext().errorIds)).toEqual(['err1']); + expect(replay.isEnabled()).toBe(true); + expect(replay.isPaused()).toBe(false); + expect(replay.recordingMode).toBe('error'); + }); +}); diff --git a/packages/replay/test/integration/coreHandlers/handleGlobalEvent.test.ts b/packages/replay/test/integration/coreHandlers/handleGlobalEvent.test.ts index 1bce8c11572a..24e709707033 100644 --- a/packages/replay/test/integration/coreHandlers/handleGlobalEvent.test.ts +++ b/packages/replay/test/integration/coreHandlers/handleGlobalEvent.test.ts @@ -1,13 +1,8 @@ -import { getCurrentHub } from '@sentry/core'; import type { Event } from '@sentry/types'; import { REPLAY_EVENT_NAME } from '../../../src/constants'; import { handleGlobalEventListener } from '../../../src/coreHandlers/handleGlobalEvent'; import type { ReplayContainer } from '../../../src/replay'; -import { - overwriteRecordDroppedEvent, - restoreRecordDroppedEvent, -} from '../../../src/util/monkeyPatchRecordDroppedEvent'; import { Error } from '../../fixtures/error'; import { Transaction } from '../../fixtures/transaction'; import { resetSdkMock } from '../../mocks/resetSdkMock'; @@ -73,11 +68,10 @@ describe('Integration | coreHandlers | handleGlobalEvent', () => { ); }); - it('only tags errors with replay id, adds trace and error id to context for error samples', async () => { + it('does not add replayId for transactions in error mode', async () => { const transaction = Transaction(); const error = Error(); - // @ts-ignore idc - expect(handleGlobalEventListener(replay)(transaction)).toEqual( + expect(handleGlobalEventListener(replay)(transaction, {})).toEqual( expect.objectContaining({ tags: expect.not.objectContaining({ replayId: expect.anything() }), }), @@ -87,37 +81,6 @@ describe('Integration | coreHandlers | handleGlobalEvent', () => { tags: expect.objectContaining({ replayId: expect.any(String) }), }), ); - - expect(replay.getContext().traceIds).toContain('trace_id'); - expect(replay.getContext().errorIds).toContain('event_id'); - - jest.runAllTimers(); - await new Promise(process.nextTick); // wait for flush - - // Rerverts `recordingMode` to session - expect(replay.recordingMode).toBe('session'); - }); - - it('strips out dropped events from errorIds', async () => { - const error1 = Error({ event_id: 'err1' }); - const error2 = Error({ event_id: 'err2' }); - const error3 = Error({ event_id: 'err3' }); - - // @ts-ignore private - overwriteRecordDroppedEvent(replay.getContext().errorIds); - - const client = getCurrentHub().getClient()!; - - handleGlobalEventListener(replay)(error1, {}); - handleGlobalEventListener(replay)(error2, {}); - handleGlobalEventListener(replay)(error3, {}); - - client.recordDroppedEvent('before_send', 'error', { event_id: 'err2' }); - - // @ts-ignore private - expect(Array.from(replay.getContext().errorIds)).toEqual(['err1', 'err3']); - - restoreRecordDroppedEvent(); }); it('tags errors and transactions with replay id for session samples', async () => { @@ -125,8 +88,7 @@ describe('Integration | coreHandlers | handleGlobalEvent', () => { replay.start(); const transaction = Transaction(); const error = Error(); - // @ts-ignore idc - expect(handleGlobalEventListener(replay)(transaction)).toEqual( + expect(handleGlobalEventListener(replay)(transaction, {})).toEqual( expect.objectContaining({ tags: expect.objectContaining({ replayId: expect.any(String) }), }), @@ -138,6 +100,88 @@ describe('Integration | coreHandlers | handleGlobalEvent', () => { ); }); + it('does not collect errorIds when hooks are available', async () => { + const error1 = Error({ event_id: 'err1' }); + const error2 = Error({ event_id: 'err2' }); + const error3 = Error({ event_id: 'err3' }); + + const handler = handleGlobalEventListener(replay); + + handler(error1, {}); + handler(error2, {}); + handler(error3, {}); + + expect(Array.from(replay.getContext().errorIds)).toEqual([]); + }); + + it('collects errorIds when hooks are not available', async () => { + const error1 = Error({ event_id: 'err1' }); + const error2 = Error({ event_id: 'err2' }); + const error3 = Error({ event_id: 'err3' }); + + const handler = handleGlobalEventListener(replay, true); + + handler(error1, {}); + handler(error2, {}); + handler(error3, {}); + + expect(Array.from(replay.getContext().errorIds)).toEqual(['err1', 'err2', 'err3']); + }); + + it('does not collect traceIds when hooks are available', async () => { + const transaction1 = Transaction('tr1'); + const transaction2 = Transaction('tr2'); + const transaction3 = Transaction('tr3'); + + const handler = handleGlobalEventListener(replay); + + handler(transaction1, {}); + handler(transaction2, {}); + handler(transaction3, {}); + + expect(Array.from(replay.getContext().traceIds)).toEqual([]); + }); + + it('collects traceIds when hooks are not available', async () => { + const transaction1 = Transaction('tr1'); + const transaction2 = Transaction('tr2'); + const transaction3 = Transaction('tr3'); + + const handler = handleGlobalEventListener(replay, true); + + handler(transaction1, {}); + handler(transaction2, {}); + handler(transaction3, {}); + + expect(Array.from(replay.getContext().traceIds)).toEqual(['tr1', 'tr2', 'tr3']); + }); + + it('ignores profile & replay events', async () => { + const profileEvent: Event = { type: 'profile' }; + const replayEvent: Event = { type: 'replay_event' }; + + const handler = handleGlobalEventListener(replay); + const handler2 = handleGlobalEventListener(replay, true); + + expect(replay.recordingMode).toBe('error'); + + handler(profileEvent, {}); + handler(replayEvent, {}); + handler2(profileEvent, {}); + handler2(replayEvent, {}); + + expect(Array.from(replay.getContext().traceIds)).toEqual([]); + expect(Array.from(replay.getContext().errorIds)).toEqual([]); + + jest.runAllTimers(); + await new Promise(process.nextTick); + + expect(Array.from(replay.getContext().errorIds)).toEqual([]); + expect(replay.isEnabled()).toBe(true); + expect(replay.isPaused()).toBe(false); + expect(replay.recordingMode).toBe('error'); + }); + it('does not skip non-rrweb errors', () => { const errorEvent: Event = { exception: { diff --git a/packages/replay/test/integration/errorSampleRate.test.ts b/packages/replay/test/integration/errorSampleRate.test.ts index d4bfa7279a95..85f154e523f4 100644 --- a/packages/replay/test/integration/errorSampleRate.test.ts +++ b/packages/replay/test/integration/errorSampleRate.test.ts @@ -63,6 +63,8 @@ describe('Integration | errorSampleRate', () => { expect(replay).not.toHaveLastSentReplay(); captureException(new Error('testing')); + + await new Promise(process.nextTick); jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); await new Promise(process.nextTick); @@ -109,7 +111,9 @@ describe('Integration | errorSampleRate', () => { }, }, }), - recordingData: JSON.stringify([{ data: { isCheckout: true }, timestamp: BASE_TIMESTAMP + 5020, type: 2 }]), + recordingData: JSON.stringify([ + { data: { isCheckout: true }, timestamp: BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY + 40, type: 2 }, + ]), }); jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); @@ -120,7 +124,7 @@ describe('Integration | errorSampleRate', () => { recordingData: JSON.stringify([ { data: { isCheckout: true }, - timestamp: BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY + 20, + timestamp: BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY + 40, type: 2, }, ]), @@ -138,11 +142,11 @@ describe('Integration | errorSampleRate', () => { recordingData: JSON.stringify([ { type: 5, - timestamp: BASE_TIMESTAMP + 10000 + 40, + timestamp: BASE_TIMESTAMP + 10000 + 60, data: { tag: 'breadcrumb', payload: { - timestamp: (BASE_TIMESTAMP + 10000 + 40) / 1000, + timestamp: (BASE_TIMESTAMP + 10000 + 60) / 1000, type: 'default', category: 'ui.click', message: '', @@ -347,6 +351,7 @@ describe('Integration | errorSampleRate', () => { captureException(new Error('testing')); + await new Promise(process.nextTick); jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); await new Promise(process.nextTick); @@ -358,7 +363,7 @@ describe('Integration | errorSampleRate', () => { // (advance timers + waiting for flush after the checkout) and // extra time is likely due to async of `addMemoryEntry()` - timestamp: (BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY + DEFAULT_FLUSH_MIN_DELAY + 20) / 1000, + timestamp: (BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY + DEFAULT_FLUSH_MIN_DELAY + 40) / 1000, error_ids: [expect.any(String)], trace_ids: [], urls: ['http://localhost/'], @@ -394,6 +399,7 @@ describe('Integration | errorSampleRate', () => { captureException(new Error('testing')); + await new Promise(process.nextTick); jest.runAllTimers(); jest.advanceTimersByTime(20); await new Promise(process.nextTick); @@ -431,6 +437,7 @@ describe('Integration | errorSampleRate', () => { captureException(new Error('testing')); + await new Promise(process.nextTick); jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); await new Promise(process.nextTick); @@ -440,6 +447,8 @@ describe('Integration | errorSampleRate', () => { mockRecord.takeFullSnapshot.mockClear(); (getCurrentHub().getClient()!.getTransport()!.send as unknown as jest.SpyInstance).mockClear(); + expect(replay).not.toHaveLastSentReplay(); + // Go idle jest.advanceTimersByTime(SESSION_IDLE_DURATION + 1); await new Promise(process.nextTick); @@ -534,6 +543,7 @@ it('sends a replay after loading the session multiple times', async () => { captureException(new Error('testing')); + await new Promise(process.nextTick); jest.advanceTimersByTime(DEFAULT_FLUSH_MIN_DELAY); await new Promise(process.nextTick); @@ -544,6 +554,6 @@ it('sends a replay after loading the session multiple times', async () => { // Latest checkout when we call `startRecording` again after uploading segment // after an error occurs (e.g. when we switch to session replay recording) expect(replay).toHaveLastSentReplay({ - recordingData: JSON.stringify([{ data: { isCheckout: true }, timestamp: BASE_TIMESTAMP + 5020, type: 2 }]), + recordingData: JSON.stringify([{ data: { isCheckout: true }, timestamp: BASE_TIMESTAMP + 5040, type: 2 }]), }); }); diff --git a/packages/replay/test/mocks/mockSdk.ts b/packages/replay/test/mocks/mockSdk.ts index 9da56061071f..af23a47cc5bc 100644 --- a/packages/replay/test/mocks/mockSdk.ts +++ b/packages/replay/test/mocks/mockSdk.ts @@ -1,4 +1,4 @@ -import type { Envelope, Transport } from '@sentry/types'; +import type { Envelope, Transport, TransportMakeRequestResponse } from '@sentry/types'; import type { Replay as ReplayIntegration } from '../../src'; import type { ReplayContainer } from '../../src/replay'; @@ -13,9 +13,21 @@ export interface MockSdkParams { } class MockTransport implements Transport { - send: (request: Envelope) => PromiseLike = jest.fn(async () => { - return; - }); + send: (request: Envelope) => PromiseLike; + + constructor() { + const send: ((request: Envelope) => PromiseLike) & { + __sentry__baseTransport__?: boolean; + } = jest.fn(async () => { + return { + statusCode: 200, + }; + }); + + send.__sentry__baseTransport__ = true; + this.send = send; + } + async flush() { return true; } diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index 71e2ac5a96d6..0ddc6a9615f2 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -10,7 +10,7 @@ import type { SdkMetadata } from './sdkmetadata'; import type { Session, SessionAggregates } from './session'; import type { Severity, SeverityLevel } from './severity'; import type { Transaction } from './transaction'; -import type { Transport } from './transport'; +import type { Transport, TransportMakeRequestResponse } from './transport'; /** * User-Facing Sentry SDK Client. @@ -163,6 +163,14 @@ export interface Client { */ on?(hook: 'beforeEnvelope', callback: (envelope: Envelope) => void): void; + /** + * Register a callback for when an event has been sent. + */ + on?( + hook: 'afterSendEvent', + callback: (event: Event, sendResponse: TransportMakeRequestResponse | void) => void, + ): void; + /** * Fire a hook event for transaction start and finish. Expects to be given a transaction as the * second argument. @@ -174,4 +182,10 @@ export interface Client { * second argument. */ emit?(hook: 'beforeEnvelope', envelope: Envelope): void; + + /* + * Fire a hook event after sending an event. Expects to be given an Event as the + * second argument. + */ + emit?(hook: 'afterSendEvent', event: Event, sendResponse: TransportMakeRequestResponse | void): void; } From 20b46a858158a07f0f08f2b8670d5197ae1601dd Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Thu, 16 Mar 2023 15:34:09 +0100 Subject: [PATCH 28/54] feat(replay): Add `request_body_size` & `response_body_size` to fetch/xhr (#7407) --- .../Breadcrumbs/fetch/get/subject.js | 5 + .../Breadcrumbs/fetch/get/test.ts | 37 ++++ .../integrations/Breadcrumbs/fetch/init.js | 10 + .../Breadcrumbs/fetch/post/subject.js | 13 ++ .../Breadcrumbs/fetch/post/test.ts | 37 ++++ .../Breadcrumbs/xhr/get/subject.js | 10 + .../integrations/Breadcrumbs/xhr/get/test.ts | 38 ++++ .../integrations/Breadcrumbs/xhr/init.js | 10 + .../Breadcrumbs/xhr/post/subject.js | 12 ++ .../integrations/Breadcrumbs/xhr/post/test.ts | 37 ++++ .../replay/errors/errorsInSession/test.ts | 8 +- .../fetch/contentLengthHeader/test.ts | 61 ++++++ .../extendNetworkBreadcrumbs/fetch/init.js | 17 ++ .../fetch/noContentLengthHeader/test.ts | 60 ++++++ .../fetch/nonTextBody/test.ts | 63 ++++++ .../fetch/requestBody/test.ts | 52 +++++ .../xhr/contentLengthHeader/test.ts | 69 +++++++ .../extendNetworkBreadcrumbs/xhr/init.js | 17 ++ .../xhr/noContentLengthHeader/test.ts | 69 +++++++ .../xhr/nonTextBody/test.ts | 66 +++++++ .../xhr/requestBody/test.ts | 56 ++++++ .../browser/src/integrations/breadcrumbs.ts | 46 ++--- packages/core/src/baseclient.ts | 8 + packages/core/src/hub.ts | 4 + packages/integrations/test/tsconfig.json | 2 +- .../coreHandlers/extendNetworkBreadcrumbs.ts | 181 ++++++++++++++++++ .../replay/src/coreHandlers/handleFetch.ts | 37 ++-- packages/replay/src/coreHandlers/handleXhr.ts | 66 ++----- .../replay/src/util/addGlobalListeners.ts | 3 + .../extendNetworkBreadcrumbs.test.ts | 63 ++++++ .../unit/coreHandlers/handleFetch.test.ts | 31 ++- .../test/unit/coreHandlers/handleXhr.test.ts | 61 ++++++ packages/types/src/client.ts | 11 ++ packages/types/src/index.ts | 2 +- packages/types/src/instrument.ts | 28 ++- packages/utils/src/instrument.ts | 45 +++-- scripts/node-unit-tests.ts | 9 +- 37 files changed, 1209 insertions(+), 135 deletions(-) create mode 100644 packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/subject.js create mode 100644 packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/test.ts create mode 100644 packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/init.js create mode 100644 packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/subject.js create mode 100644 packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/test.ts create mode 100644 packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/subject.js create mode 100644 packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/test.ts create mode 100644 packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/init.js create mode 100644 packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/subject.js create mode 100644 packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/test.ts create mode 100644 packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/contentLengthHeader/test.ts create mode 100644 packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/init.js create mode 100644 packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/noContentLengthHeader/test.ts create mode 100644 packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/nonTextBody/test.ts create mode 100644 packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/requestBody/test.ts create mode 100644 packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/contentLengthHeader/test.ts create mode 100644 packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/init.js create mode 100644 packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/noContentLengthHeader/test.ts create mode 100644 packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/nonTextBody/test.ts create mode 100644 packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/requestBody/test.ts create mode 100644 packages/replay/src/coreHandlers/extendNetworkBreadcrumbs.ts create mode 100644 packages/replay/test/unit/coreHandlers/extendNetworkBreadcrumbs.test.ts create mode 100644 packages/replay/test/unit/coreHandlers/handleXhr.test.ts diff --git a/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/subject.js b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/subject.js new file mode 100644 index 000000000000..fc9ffd720768 --- /dev/null +++ b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/subject.js @@ -0,0 +1,5 @@ +const xhr = new XMLHttpRequest(); + +fetch('http://localhost:7654/foo').then(() => { + Sentry.captureException('test error'); +}); diff --git a/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/test.ts b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/test.ts new file mode 100644 index 000000000000..c836a8e46943 --- /dev/null +++ b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/test.ts @@ -0,0 +1,37 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb for basic GET request', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData!.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'fetch', + type: 'http', + data: { + method: 'GET', + status_code: 200, + url: 'http://localhost:7654/foo', + }, + }); +}); diff --git a/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/init.js b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/init.js new file mode 100644 index 000000000000..9bd2d9649ed8 --- /dev/null +++ b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/init.js @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + defaultIntegrations: false, + integrations: [new Sentry.Integrations.Breadcrumbs()], + sampleRate: 1, +}); diff --git a/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/subject.js b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/subject.js new file mode 100644 index 000000000000..595e9395aa80 --- /dev/null +++ b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/subject.js @@ -0,0 +1,13 @@ +const xhr = new XMLHttpRequest(); + +fetch('http://localhost:7654/foo', { + method: 'POST', + body: '{"my":"body"}', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Cache: 'no-cache', + }, +}).then(() => { + Sentry.captureException('test error'); +}); diff --git a/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/test.ts b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/test.ts new file mode 100644 index 000000000000..1635ce394ae5 --- /dev/null +++ b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/test.ts @@ -0,0 +1,37 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb for POST request', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData!.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'fetch', + type: 'http', + data: { + method: 'POST', + status_code: 200, + url: 'http://localhost:7654/foo', + }, + }); +}); diff --git a/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/subject.js b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/subject.js new file mode 100644 index 000000000000..f95bcb45c166 --- /dev/null +++ b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/subject.js @@ -0,0 +1,10 @@ +const xhr = new XMLHttpRequest(); + +xhr.open('GET', 'http://localhost:7654/foo'); +xhr.send(); + +xhr.addEventListener('readystatechange', function () { + if (xhr.readyState === 4) { + Sentry.captureException('test error'); + } +}); diff --git a/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/test.ts b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/test.ts new file mode 100644 index 000000000000..dd1be663cbb4 --- /dev/null +++ b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/test.ts @@ -0,0 +1,38 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb for basic GET request', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + 'Content-Length': '', + }, + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData!.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'xhr', + type: 'http', + data: { + method: 'GET', + status_code: 200, + url: 'http://localhost:7654/foo', + }, + }); +}); diff --git a/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/init.js b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/init.js new file mode 100644 index 000000000000..9bd2d9649ed8 --- /dev/null +++ b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/init.js @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + defaultIntegrations: false, + integrations: [new Sentry.Integrations.Breadcrumbs()], + sampleRate: 1, +}); diff --git a/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/subject.js b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/subject.js new file mode 100644 index 000000000000..f8779b681d58 --- /dev/null +++ b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/subject.js @@ -0,0 +1,12 @@ +const xhr = new XMLHttpRequest(); + +xhr.open('POST', 'http://localhost:7654/foo'); +xhr.setRequestHeader('Accept', 'application/json'); +xhr.setRequestHeader('Content-Type', 'application/json'); +xhr.send('{"my":"body"}'); + +xhr.addEventListener('readystatechange', function () { + if (xhr.readyState === 4) { + Sentry.captureException('test error'); + } +}); diff --git a/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/test.ts b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/test.ts new file mode 100644 index 000000000000..5a53328502c7 --- /dev/null +++ b/packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/test.ts @@ -0,0 +1,37 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb for POST request', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData!.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'xhr', + type: 'http', + data: { + method: 'POST', + status_code: 200, + url: 'http://localhost:7654/foo', + }, + }); +}); diff --git a/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts b/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts index 80d36b6688e2..3badb2b956cb 100644 --- a/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts +++ b/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts @@ -12,7 +12,7 @@ import { sentryTest( '[session-mode] replay event should contain an error id of an error that occurred during session recording', - async ({ getLocalTestPath, page, browserName }) => { + async ({ getLocalTestPath, page, browserName, forceFlushReplay }) => { // TODO(replay): This is flakey on firefox where clicks are flakey if (shouldSkipReplayTest() || ['firefox'].includes(browserName)) { sentryTest.skip(); @@ -43,7 +43,7 @@ sentryTest( const req0 = await reqPromise0; await page.click('#error'); - await page.click('#go-background'); + await forceFlushReplay(); const req1 = await reqPromise1; const event0 = getReplayEvent(req0); @@ -86,7 +86,7 @@ sentryTest( sentryTest( '[session-mode] replay event should not contain an error id of a dropped error while recording', - async ({ getLocalTestPath, page }) => { + async ({ getLocalTestPath, page, forceFlushReplay }) => { if (shouldSkipReplayTest()) { sentryTest.skip(); } @@ -108,7 +108,7 @@ sentryTest( await reqPromise0; await page.click('#drop'); - await page.click('#go-background'); + await forceFlushReplay(); const req1 = await reqPromise1; const event1 = getReplayEvent(req1); diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/contentLengthHeader/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/contentLengthHeader/test.ts new file mode 100644 index 000000000000..1ffeb360c650 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/contentLengthHeader/test.ts @@ -0,0 +1,61 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers'; +import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers'; + +sentryTest('parses response_body_size from Content-Length header if available', async ({ getLocalTestPath, page }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + 'Content-Length': '789', + }, + }); + }); + + const requestPromise = waitForErrorRequest(page); + const url = await getLocalTestPath({ testDir: __dirname }); + await page.goto(url); + + await page.evaluate(() => { + /* eslint-disable */ + fetch('http://localhost:7654/foo', { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Cache: 'no-cache', + }, + }).then(() => { + // @ts-ignore Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }); + + const request = await requestPromise; + const eventData = envelopeRequestParser(request); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData!.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'fetch', + type: 'http', + data: { + method: 'GET', + response_body_size: 789, + status_code: 200, + url: 'http://localhost:7654/foo', + }, + }); +}); diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/init.js b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/init.js new file mode 100644 index 000000000000..6f80c5e4cb8f --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/init.js @@ -0,0 +1,17 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window.Replay = new Sentry.Replay({ + flushMinDelay: 200, + flushMaxDelay: 200, +}); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 1, + // We ensure to sample for errors, so by default nothing is sent + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + + integrations: [window.Replay], +}); diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/noContentLengthHeader/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/noContentLengthHeader/test.ts new file mode 100644 index 000000000000..8248b4799480 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/noContentLengthHeader/test.ts @@ -0,0 +1,60 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers'; +import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers'; + +sentryTest('does not capture response_body_size without Content-Length header', async ({ getLocalTestPath, page }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + 'Content-Length': '', + }, + }); + }); + + const requestPromise = waitForErrorRequest(page); + const url = await getLocalTestPath({ testDir: __dirname }); + await page.goto(url); + + await page.evaluate(() => { + /* eslint-disable */ + fetch('http://localhost:7654/foo', { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Cache: 'no-cache', + }, + }).then(() => { + // @ts-ignore Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }); + + const request = await requestPromise; + const eventData = envelopeRequestParser(request); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData!.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'fetch', + type: 'http', + data: { + method: 'GET', + status_code: 200, + url: 'http://localhost:7654/foo', + }, + }); +}); diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/nonTextBody/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/nonTextBody/test.ts new file mode 100644 index 000000000000..a293df49b366 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/nonTextBody/test.ts @@ -0,0 +1,63 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers'; +import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers'; + +sentryTest('calculates body sizes for non-string bodies', async ({ getLocalTestPath, page }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + await page.route('**/foo', async route => { + return route.fulfill({ + status: 200, + body: Buffer.from('Hello world'), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const requestPromise = waitForErrorRequest(page); + const url = await getLocalTestPath({ testDir: __dirname }); + await page.goto(url); + + await page.evaluate(() => { + /* eslint-disable */ + const blob = new Blob(['Hello world!!'], { type: 'text/html' }); + + fetch('http://localhost:7654/foo', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Cache: 'no-cache', + }, + body: blob, + }).then(() => { + // @ts-ignore Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }); + + const request = await requestPromise; + const eventData = envelopeRequestParser(request); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData!.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'fetch', + type: 'http', + data: { + method: 'POST', + request_body_size: 26, + response_body_size: 24, + status_code: 200, + url: 'http://localhost:7654/foo', + }, + }); +}); diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/requestBody/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/requestBody/test.ts new file mode 100644 index 000000000000..baac9005fd35 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/requestBody/test.ts @@ -0,0 +1,52 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers'; +import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers'; + +sentryTest('captures request_body_size when body is sent', async ({ getLocalTestPath, page }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const requestPromise = waitForErrorRequest(page); + const url = await getLocalTestPath({ testDir: __dirname }); + await page.goto(url); + + await page.evaluate(() => { + /* eslint-disable */ + fetch('http://localhost:7654/foo', { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Cache: 'no-cache', + }, + body: '{"foo":"bar"}', + }).then(() => { + // @ts-ignore Sentry is a global + Sentry.captureException('test error'); + }); + /* eslint-enable */ + }); + + const request = await requestPromise; + const eventData = envelopeRequestParser(request); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData!.breadcrumbs![0].data!.request_body_size).toEqual(13); +}); diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/contentLengthHeader/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/contentLengthHeader/test.ts new file mode 100644 index 000000000000..b5f517f77352 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/contentLengthHeader/test.ts @@ -0,0 +1,69 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers'; +import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers'; + +sentryTest( + 'parses response_body_size from Content-Length header if available', + async ({ getLocalTestPath, page, browserName }) => { + // These are a bit flaky on non-chromium browsers + if (shouldSkipReplayTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + 'Content-Length': '789', + }, + }); + }); + + const requestPromise = waitForErrorRequest(page); + const url = await getLocalTestPath({ testDir: __dirname }); + await page.goto(url); + + await page.evaluate(() => { + /* eslint-disable */ + const xhr = new XMLHttpRequest(); + + xhr.open('GET', 'http://localhost:7654/foo'); + xhr.setRequestHeader('Accept', 'application/json'); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.setRequestHeader('Cache', 'no-cache'); + xhr.send(); + + xhr.addEventListener('readystatechange', function () { + if (xhr.readyState === 4) { + // @ts-ignore Sentry is a global + setTimeout(() => Sentry.captureException('test error', 0)); + } + }); + /* eslint-enable */ + }); + + const request = await requestPromise; + const eventData = envelopeRequestParser(request); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData!.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'xhr', + type: 'http', + data: { + method: 'GET', + response_body_size: 789, + status_code: 200, + url: 'http://localhost:7654/foo', + }, + }); + }, +); diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/init.js b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/init.js new file mode 100644 index 000000000000..6f80c5e4cb8f --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/init.js @@ -0,0 +1,17 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window.Replay = new Sentry.Replay({ + flushMinDelay: 200, + flushMaxDelay: 200, +}); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 1, + // We ensure to sample for errors, so by default nothing is sent + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + + integrations: [window.Replay], +}); diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/noContentLengthHeader/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/noContentLengthHeader/test.ts new file mode 100644 index 000000000000..9ea10831afab --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/noContentLengthHeader/test.ts @@ -0,0 +1,69 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers'; +import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers'; + +sentryTest( + 'captures response_body_size without Content-Length header', + async ({ getLocalTestPath, page, browserName }) => { + // These are a bit flaky on non-chromium browsers + if (shouldSkipReplayTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + 'Content-Length': '', + }, + }); + }); + + const requestPromise = waitForErrorRequest(page); + const url = await getLocalTestPath({ testDir: __dirname }); + await page.goto(url); + + await page.evaluate(() => { + /* eslint-disable */ + const xhr = new XMLHttpRequest(); + + xhr.open('GET', 'http://localhost:7654/foo'); + xhr.setRequestHeader('Accept', 'application/json'); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.setRequestHeader('Cache', 'no-cache'); + xhr.send(); + + xhr.addEventListener('readystatechange', function () { + if (xhr.readyState === 4) { + // @ts-ignore Sentry is a global + setTimeout(() => Sentry.captureException('test error', 0)); + } + }); + /* eslint-enable */ + }); + + const request = await requestPromise; + const eventData = envelopeRequestParser(request); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData!.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'xhr', + type: 'http', + data: { + method: 'GET', + response_body_size: 29, + status_code: 200, + url: 'http://localhost:7654/foo', + }, + }); + }, +); diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/nonTextBody/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/nonTextBody/test.ts new file mode 100644 index 000000000000..5142f2e6be82 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/nonTextBody/test.ts @@ -0,0 +1,66 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers'; +import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers'; + +sentryTest('calculates body sizes for non-string bodies', async ({ getLocalTestPath, page, browserName }) => { + // These are a bit flaky on non-chromium browsers + if (shouldSkipReplayTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + await page.route('**/foo', async route => { + return route.fulfill({ + status: 200, + body: Buffer.from('Hello world'), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const requestPromise = waitForErrorRequest(page); + const url = await getLocalTestPath({ testDir: __dirname }); + await page.goto(url); + + await page.evaluate(() => { + /* eslint-disable */ + const xhr = new XMLHttpRequest(); + + const blob = new Blob(['Hello world!!'], { type: 'text/html' }); + + xhr.open('POST', 'http://localhost:7654/foo'); + xhr.setRequestHeader('Accept', 'application/json'); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.setRequestHeader('Cache', 'no-cache'); + xhr.send(blob); + + xhr.addEventListener('readystatechange', function () { + if (xhr.readyState === 4) { + // @ts-ignore Sentry is a global + setTimeout(() => Sentry.captureException('test error', 0)); + } + }); + /* eslint-enable */ + }); + + const request = await requestPromise; + const eventData = envelopeRequestParser(request); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData!.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'xhr', + type: 'http', + data: { + method: 'POST', + request_body_size: 26, + response_body_size: 24, + status_code: 200, + url: 'http://localhost:7654/foo', + }, + }); +}); diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/requestBody/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/requestBody/test.ts new file mode 100644 index 000000000000..fd3cc426f9fd --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/requestBody/test.ts @@ -0,0 +1,56 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers'; +import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers'; + +sentryTest('captures request_body_size when body is sent', async ({ getLocalTestPath, page, browserName }) => { + // These are a bit flaky on non-chromium browsers + if (shouldSkipReplayTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + 'Content-Length': '', + }, + }); + }); + + const requestPromise = waitForErrorRequest(page); + const url = await getLocalTestPath({ testDir: __dirname }); + await page.goto(url); + + void page.evaluate(() => { + /* eslint-disable */ + const xhr = new XMLHttpRequest(); + + xhr.open('POST', 'http://localhost:7654/foo'); + xhr.setRequestHeader('Accept', 'application/json'); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.setRequestHeader('Cache', 'no-cache'); + xhr.send('{"foo":"bar"}'); + + xhr.addEventListener('readystatechange', function () { + if (xhr.readyState === 4) { + // @ts-ignore Sentry is a global + setTimeout(() => Sentry.captureException('test error', 0)); + } + }); + /* eslint-enable */ + }); + + const request = await requestPromise; + const eventData = envelopeRequestParser(request); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData!.breadcrumbs![0].data!.request_body_size).toEqual(13); +}); diff --git a/packages/browser/src/integrations/breadcrumbs.ts b/packages/browser/src/integrations/breadcrumbs.ts index dd381f1f3549..ce79b89ea78d 100644 --- a/packages/browser/src/integrations/breadcrumbs.ts +++ b/packages/browser/src/integrations/breadcrumbs.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable max-lines */ import { getCurrentHub } from '@sentry/core'; -import type { Event as SentryEvent, HandlerDataFetch, Integration, SentryWrappedXMLHttpRequest } from '@sentry/types'; +import type { Event as SentryEvent, HandlerDataFetch, HandlerDataXhr, Integration } from '@sentry/types'; import { addInstrumentationHandler, getEventDescription, @@ -216,33 +216,29 @@ function _consoleBreadcrumb(handlerData: HandlerData & { args: unknown[]; level: /** * Creates breadcrumbs from XHR API calls */ -function _xhrBreadcrumb(handlerData: HandlerData & { xhr: XMLHttpRequest & SentryWrappedXMLHttpRequest }): void { - if (handlerData.endTimestamp) { - // We only capture complete, non-sentry requests - if (handlerData.xhr.__sentry_own_request__) { - return; - } +function _xhrBreadcrumb(handlerData: HandlerData & HandlerDataXhr): void { + // We only capture complete, non-sentry requests + if (!handlerData.endTimestamp || !handlerData.xhr.__sentry_xhr__) { + return; + } - const { method, url, status_code, body } = handlerData.xhr.__sentry_xhr__ || {}; + const { method, url, status_code, body } = handlerData.xhr.__sentry_xhr__; - getCurrentHub().addBreadcrumb( - { - category: 'xhr', - data: { - method, - url, - status_code, - }, - type: 'http', + getCurrentHub().addBreadcrumb( + { + category: 'xhr', + data: { + method, + url, + status_code, }, - { - xhr: handlerData.xhr, - input: body, - }, - ); - - return; - } + type: 'http', + }, + { + xhr: handlerData.xhr, + input: body, + }, + ); } /** diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index 80ed36450ef5..1b9141b14fe0 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -1,5 +1,7 @@ /* eslint-disable max-lines */ import type { + Breadcrumb, + BreadcrumbHint, Client, ClientOptions, DataCategory, @@ -373,6 +375,9 @@ export abstract class BaseClient implements Client { callback: (event: Event, sendResponse: TransportMakeRequestResponse | void) => void, ): void; + /** @inheritdoc */ + public on(hook: 'beforeAddBreadcrumb', callback: (breadcrumb: Breadcrumb, hint?: BreadcrumbHint) => void): void; + /** @inheritdoc */ public on(hook: string, callback: unknown): void { if (!this._hooks[hook]) { @@ -392,6 +397,9 @@ export abstract class BaseClient implements Client { /** @inheritdoc */ public emit(hook: 'afterSendEvent', event: Event, sendResponse: TransportMakeRequestResponse | void): void; + /** @inheritdoc */ + public emit(hook: 'beforeAddBreadcrumb', breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void; + /** @inheritdoc */ public emit(hook: string, ...rest: unknown[]): void { if (this._hooks[hook]) { diff --git a/packages/core/src/hub.ts b/packages/core/src/hub.ts index c150c05eaef9..203241a35389 100644 --- a/packages/core/src/hub.ts +++ b/packages/core/src/hub.ts @@ -271,6 +271,10 @@ export class Hub implements HubInterface { if (finalBreadcrumb === null) return; + if (client.emit) { + client.emit('beforeAddBreadcrumb', finalBreadcrumb, hint); + } + scope.addBreadcrumb(finalBreadcrumb, maxBreadcrumbs); } diff --git a/packages/integrations/test/tsconfig.json b/packages/integrations/test/tsconfig.json index 074ceb45a9db..c4023463fcbc 100644 --- a/packages/integrations/test/tsconfig.json +++ b/packages/integrations/test/tsconfig.json @@ -4,5 +4,5 @@ { "extends": "../tsconfig.test.json", - "include": ["./**/*"] + "include": ["./**/*", "../../replay/test/unit/coreHandlers/extendednetworkbreadcrumbs.test.ts"] } diff --git a/packages/replay/src/coreHandlers/extendNetworkBreadcrumbs.ts b/packages/replay/src/coreHandlers/extendNetworkBreadcrumbs.ts new file mode 100644 index 000000000000..bc1acb1c7c72 --- /dev/null +++ b/packages/replay/src/coreHandlers/extendNetworkBreadcrumbs.ts @@ -0,0 +1,181 @@ +import { getCurrentHub } from '@sentry/core'; +import type { + Breadcrumb, + BreadcrumbHint, + HandlerDataFetch, + SentryWrappedXMLHttpRequest, + TextEncoderInternal, +} from '@sentry/types'; +import { logger } from '@sentry/utils'; + +type RequestBody = null | Blob | BufferSource | FormData | URLSearchParams | string; + +interface ExtendedNetworkBreadcrumbsOptions { + textEncoder: TextEncoderInternal; +} + +/** + * This will enrich the xhr/fetch breadcrumbs with additional information. + * + * This adds: + * * request_body_size + * * response_body_size + * + * to the breadcrumb data. + */ +export function extendNetworkBreadcrumbs(): void { + const client = getCurrentHub().getClient(); + + try { + const textEncoder = new TextEncoder(); + + const options: ExtendedNetworkBreadcrumbsOptions = { + textEncoder, + }; + + if (client && client.on) { + client.on('beforeAddBreadcrumb', (breadcrumb, hint) => _beforeNetworkBreadcrumb(options, breadcrumb, hint)); + } + } catch { + // Do nothing + } +} + +function _beforeNetworkBreadcrumb( + options: ExtendedNetworkBreadcrumbsOptions, + breadcrumb: Breadcrumb, + hint?: BreadcrumbHint, +): void { + if (!breadcrumb.data) { + return; + } + + try { + if (breadcrumb.category === 'xhr' && hint && hint.xhr) { + _enrichXhrBreadcrumb( + breadcrumb as Breadcrumb & { data: object }, + { + xhr: hint.xhr as XMLHttpRequest & SentryWrappedXMLHttpRequest, + body: hint.input as RequestBody, + }, + options, + ); + } + + if (breadcrumb.category === 'fetch' && hint) { + _enrichFetchBreadcrumb( + breadcrumb as Breadcrumb & { data: object }, + { + input: hint.input as HandlerDataFetch['args'], + response: hint.response as Response, + }, + options, + ); + } + } catch (e) { + __DEBUG_BUILD__ && logger.warn('Error when enriching network breadcrumb'); + } +} + +function _enrichXhrBreadcrumb( + breadcrumb: Breadcrumb & { data: object }, + hint: { xhr: XMLHttpRequest & SentryWrappedXMLHttpRequest; body?: RequestBody }, + options: ExtendedNetworkBreadcrumbsOptions, +): void { + const { xhr, body } = hint; + + const reqSize = getBodySize(body, options.textEncoder); + const resSize = xhr.getResponseHeader('content-length') + ? parseContentSizeHeader(xhr.getResponseHeader('content-length')) + : getBodySize(xhr.response, options.textEncoder); + + if (reqSize !== undefined) { + breadcrumb.data.request_body_size = reqSize; + } + if (resSize !== undefined) { + breadcrumb.data.response_body_size = resSize; + } +} + +function _enrichFetchBreadcrumb( + breadcrumb: Breadcrumb & { data: object }, + hint: { + input: HandlerDataFetch['args']; + response: Response; + }, + options: ExtendedNetworkBreadcrumbsOptions, +): void { + const { input, response } = hint; + + const body = getFetchBody(input); + const reqSize = getBodySize(body, options.textEncoder); + const resSize = response ? parseContentSizeHeader(response.headers.get('content-length')) : undefined; + + if (reqSize !== undefined) { + breadcrumb.data.request_body_size = reqSize; + } + if (resSize !== undefined) { + breadcrumb.data.response_body_size = resSize; + } +} + +/** only exported for tests */ +export function getBodySize( + body: RequestInit['body'], + textEncoder: TextEncoder | TextEncoderInternal, +): number | undefined { + if (!body) { + return undefined; + } + + try { + if (typeof body === 'string') { + return textEncoder.encode(body).length; + } + + if (body instanceof URLSearchParams) { + return textEncoder.encode(body.toString()).length; + } + + if (body instanceof FormData) { + // This is a bit simplified, but gives us a decent estimate + // This converts e.g. { name: 'Anne Smith', age: 13 } to 'name=Anne+Smith&age=13' + // @ts-ignore passing FormData to URLSearchParams actually works + const formDataStr = new URLSearchParams(body).toString(); + return textEncoder.encode(formDataStr).length; + } + + if (body instanceof Blob) { + return body.size; + } + + if (body instanceof ArrayBuffer) { + return body.byteLength; + } + + // Currently unhandled types: ArrayBufferView, ReadableStream + } catch { + // just return undefined + } + + return undefined; +} + +/** only exported for tests */ +export function parseContentSizeHeader(header: string | null | undefined): number | undefined { + if (!header) { + return undefined; + } + + const size = parseInt(header, 10); + return isNaN(size) ? undefined : size; +} + +function getFetchBody(fetchArgs: unknown[] = []): RequestInit['body'] | undefined { + // We only support getting the body from the fetch options + if (fetchArgs.length !== 2 || typeof fetchArgs[1] !== 'object') { + return undefined; + } + + return (fetchArgs[1] as RequestInit).body; +} diff --git a/packages/replay/src/coreHandlers/handleFetch.ts b/packages/replay/src/coreHandlers/handleFetch.ts index 290a58d4531d..4ba163d5c5c9 100644 --- a/packages/replay/src/coreHandlers/handleFetch.ts +++ b/packages/replay/src/coreHandlers/handleFetch.ts @@ -1,31 +1,18 @@ +import type { HandlerDataFetch } from '@sentry/types'; + import type { ReplayContainer, ReplayPerformanceEntry } from '../types'; import { createPerformanceSpans } from '../util/createPerformanceSpans'; import { shouldFilterRequest } from '../util/shouldFilterRequest'; -interface FetchHandlerData { - args: Parameters; - fetchData: { - method: string; - url: string; - }; - response: { - type: string; - url: string; - redirected: boolean; - status: number; - ok: boolean; - }; - startTimestamp: number; - endTimestamp?: number; -} - /** only exported for tests */ -export function handleFetch(handlerData: FetchHandlerData): null | ReplayPerformanceEntry { - if (!handlerData.endTimestamp) { +export function handleFetch(handlerData: HandlerDataFetch): null | ReplayPerformanceEntry { + const { startTimestamp, endTimestamp, fetchData, response } = handlerData; + + if (!endTimestamp) { return null; } - const { startTimestamp, endTimestamp, fetchData, response } = handlerData; + const { method, request_body_size: requestBodySize, response_body_size: responseBodySize } = fetchData; return { type: 'resource.fetch', @@ -33,8 +20,10 @@ export function handleFetch(handlerData: FetchHandlerData): null | ReplayPerform end: endTimestamp / 1000, name: fetchData.url, data: { - method: fetchData.method, - statusCode: response.status, + method, + statusCode: response && (response as Response).status, + requestBodySize, + responseBodySize, }, }; } @@ -42,8 +31,8 @@ export function handleFetch(handlerData: FetchHandlerData): null | ReplayPerform /** * Returns a listener to be added to `addInstrumentationHandler('fetch', listener)`. */ -export function handleFetchSpanListener(replay: ReplayContainer): (handlerData: FetchHandlerData) => void { - return (handlerData: FetchHandlerData) => { +export function handleFetchSpanListener(replay: ReplayContainer): (handlerData: HandlerDataFetch) => void { + return (handlerData: HandlerDataFetch) => { if (!replay.isEnabled()) { return; } diff --git a/packages/replay/src/coreHandlers/handleXhr.ts b/packages/replay/src/coreHandlers/handleXhr.ts index 406a4d3dc175..1e39ec409ea8 100644 --- a/packages/replay/src/coreHandlers/handleXhr.ts +++ b/packages/replay/src/coreHandlers/handleXhr.ts @@ -1,67 +1,39 @@ +import type { HandlerDataXhr } from '@sentry/types'; + import type { ReplayContainer, ReplayPerformanceEntry } from '../types'; import { createPerformanceSpans } from '../util/createPerformanceSpans'; import { shouldFilterRequest } from '../util/shouldFilterRequest'; -// From sentry-javascript -// e.g. https://github.com/getsentry/sentry-javascript/blob/c7fc025bf9fa8c073fdb56351808ce53909fbe45/packages/utils/src/instrument.ts#L180 -type XHRSendInput = null | Blob | BufferSource | FormData | URLSearchParams | string; - -interface SentryWrappedXMLHttpRequest extends XMLHttpRequest { - [key: string]: unknown; - __sentry_xhr__?: { - method?: string; - url?: string; - status_code?: number; - body?: XHRSendInput; - startTimestamp?: number; // This is unique to replay SDK - }; - // If Sentry key appears in request, don't capture as request - // See https://github.com/getsentry/sentry-javascript/blob/c7fc025bf9fa8c073fdb56351808ce53909fbe45/packages/utils/src/instrument.ts#L236 - __sentry_own_request__?: boolean; -} - -interface XhrHandlerData { - args: [string, string]; - xhr: SentryWrappedXMLHttpRequest; - startTimestamp: number; - endTimestamp?: number; -} - -function handleXhr(handlerData: XhrHandlerData): ReplayPerformanceEntry | null { - if (handlerData.xhr.__sentry_own_request__) { - // Taken from sentry-javascript - // Only capture non-sentry requests - return null; - } - - if (handlerData.startTimestamp) { - // TODO: See if this is still needed - handlerData.xhr.__sentry_xhr__ = handlerData.xhr.__sentry_xhr__ || {}; - handlerData.xhr.__sentry_xhr__.startTimestamp = handlerData.startTimestamp; - } +/** only exported for tests */ +export function handleXhr(handlerData: HandlerDataXhr): ReplayPerformanceEntry | null { + const { startTimestamp, endTimestamp, xhr } = handlerData; - if (!handlerData.endTimestamp) { + if (!startTimestamp || !endTimestamp || !xhr.__sentry_xhr__) { return null; } - const { method, url, status_code: statusCode } = handlerData.xhr.__sentry_xhr__ || {}; + const { + method, + url, + status_code: statusCode, + request_body_size: requestBodySize, + response_body_size: responseBodySize, + } = xhr.__sentry_xhr__; if (url === undefined) { return null; } - const timestamp = handlerData.xhr.__sentry_xhr__ - ? handlerData.xhr.__sentry_xhr__.startTimestamp || 0 - : handlerData.endTimestamp; - return { type: 'resource.xhr', name: url, - start: timestamp / 1000, - end: handlerData.endTimestamp / 1000, + start: startTimestamp / 1000, + end: endTimestamp / 1000, data: { method, statusCode, + requestBodySize, + responseBodySize, }, }; } @@ -69,8 +41,8 @@ function handleXhr(handlerData: XhrHandlerData): ReplayPerformanceEntry | null { /** * Returns a listener to be added to `addInstrumentationHandler('xhr', listener)`. */ -export function handleXhrSpanListener(replay: ReplayContainer): (handlerData: XhrHandlerData) => void { - return (handlerData: XhrHandlerData) => { +export function handleXhrSpanListener(replay: ReplayContainer): (handlerData: HandlerDataXhr) => void { + return (handlerData: HandlerDataXhr) => { if (!replay.isEnabled()) { return; } diff --git a/packages/replay/src/util/addGlobalListeners.ts b/packages/replay/src/util/addGlobalListeners.ts index 94146962911f..ea685c49241a 100644 --- a/packages/replay/src/util/addGlobalListeners.ts +++ b/packages/replay/src/util/addGlobalListeners.ts @@ -2,6 +2,7 @@ import type { BaseClient } from '@sentry/core'; import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; import { addInstrumentationHandler } from '@sentry/utils'; +import { extendNetworkBreadcrumbs } from '../coreHandlers/extendNetworkBreadcrumbs'; import { handleAfterSendEvent } from '../coreHandlers/handleAfterSendEvent'; import { handleDomListener } from '../coreHandlers/handleDom'; import { handleFetchSpanListener } from '../coreHandlers/handleFetch'; @@ -38,4 +39,6 @@ export function addGlobalListeners(replay: ReplayContainer): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any (client as BaseClient).on('afterSendEvent', handleAfterSendEvent(replay)); } + + extendNetworkBreadcrumbs(); } diff --git a/packages/replay/test/unit/coreHandlers/extendNetworkBreadcrumbs.test.ts b/packages/replay/test/unit/coreHandlers/extendNetworkBreadcrumbs.test.ts new file mode 100644 index 000000000000..a8d6885d43c2 --- /dev/null +++ b/packages/replay/test/unit/coreHandlers/extendNetworkBreadcrumbs.test.ts @@ -0,0 +1,63 @@ +import { TextEncoder } from 'util'; + +import { getBodySize, parseContentSizeHeader } from '../../../src/coreHandlers/extendNetworkBreadcrumbs'; + +describe('Unit | coreHandlers | extendNetworkBreadcrumbs', () => { + describe('parseContentSizeHeader()', () => { + it.each([ + [undefined, undefined], + [null, undefined], + ['', undefined], + ['12', 12], + ['abc', undefined], + ])('works with %s header value', (headerValue, size) => { + expect(parseContentSizeHeader(headerValue)).toBe(size); + }); + }); + + describe('getBodySize()', () => { + const textEncoder = new TextEncoder(); + + it('works with empty body', () => { + expect(getBodySize(undefined, textEncoder)).toBe(undefined); + expect(getBodySize(null, textEncoder)).toBe(undefined); + expect(getBodySize('', textEncoder)).toBe(undefined); + }); + + it('works with string body', () => { + expect(getBodySize('abcd', textEncoder)).toBe(4); + // Emojis are correctly counted as mutliple characters + expect(getBodySize('With emoji: 😈', textEncoder)).toBe(16); + }); + + it('works with URLSearchParams', () => { + const params = new URLSearchParams(); + params.append('name', 'Jane'); + params.append('age', '42'); + params.append('emoji', '😈'); + + expect(getBodySize(params, textEncoder)).toBe(35); + }); + + it('works with FormData', () => { + const formData = new FormData(); + formData.append('name', 'Jane'); + formData.append('age', '42'); + formData.append('emoji', '😈'); + + expect(getBodySize(formData, textEncoder)).toBe(35); + }); + + it('works with Blob', () => { + const blob = new Blob(['Hello world: 😈'], { type: 'text/html' }); + + expect(getBodySize(blob, textEncoder)).toBe(30); + }); + + it('works with ArrayBuffer', () => { + const arrayBuffer = new ArrayBuffer(8); + + expect(getBodySize(arrayBuffer, textEncoder)).toBe(8); + }); + }); +}); diff --git a/packages/replay/test/unit/coreHandlers/handleFetch.test.ts b/packages/replay/test/unit/coreHandlers/handleFetch.test.ts index e936b693af56..55acbda699ad 100644 --- a/packages/replay/test/unit/coreHandlers/handleFetch.test.ts +++ b/packages/replay/test/unit/coreHandlers/handleFetch.test.ts @@ -1,6 +1,8 @@ +import type { HandlerDataFetch } from '@sentry/types'; + import { handleFetch } from '../../../src/coreHandlers/handleFetch'; -const DEFAULT_DATA = { +const DEFAULT_DATA: HandlerDataFetch = { args: ['/api/0/organizations/sentry/', { method: 'GET', headers: {}, credentials: 'include' }] as Parameters< typeof fetch >, @@ -15,7 +17,7 @@ const DEFAULT_DATA = { redirected: false, status: 200, ok: true, - }, + } as Response, startTimestamp: 10000, }; @@ -36,13 +38,28 @@ describe('Unit | coreHandlers | handleFetch', () => { it('ignores fetches that have not completed yet', function () { const data = { ...DEFAULT_DATA, + endTimestamp: undefined, + response: undefined, }; - // @ts-ignore: The operand of a 'delete' operator must be optional.ts(2790) - delete data.endTimestamp; - // @ts-ignore: The operand of a 'delete' operator must be optional.ts(2790) - delete data.response; - expect(handleFetch(data)).toEqual(null); }); + + it('passes request/response size through if available', function () { + const data = { + ...DEFAULT_DATA, + fetchData: { + ...DEFAULT_DATA.fetchData, + request_body_size: 123, + response_body_size: 456, + }, + }; + + expect(handleFetch(data)?.data).toEqual({ + method: 'GET', + statusCode: 200, + requestBodySize: 123, + responseBodySize: 456, + }); + }); }); diff --git a/packages/replay/test/unit/coreHandlers/handleXhr.test.ts b/packages/replay/test/unit/coreHandlers/handleXhr.test.ts new file mode 100644 index 000000000000..536b6edc6896 --- /dev/null +++ b/packages/replay/test/unit/coreHandlers/handleXhr.test.ts @@ -0,0 +1,61 @@ +import type { HandlerDataXhr, SentryWrappedXMLHttpRequest } from '@sentry/types'; + +import { handleXhr } from '../../../src/coreHandlers/handleXhr'; + +const DEFAULT_DATA: HandlerDataXhr = { + args: ['GET', '/api/0/organizations/sentry/'], + xhr: { + __sentry_xhr__: { + method: 'GET', + url: '/api/0/organizations/sentry/', + status_code: 200, + }, + } as SentryWrappedXMLHttpRequest, + startTimestamp: 10000, + endTimestamp: 15000, +}; + +describe('Unit | coreHandlers | handleXhr', () => { + it('formats fetch calls from core SDK to replay breadcrumbs', function () { + expect(handleXhr(DEFAULT_DATA)).toEqual({ + type: 'resource.xhr', + name: '/api/0/organizations/sentry/', + start: 10, + end: 15, + data: { + method: 'GET', + statusCode: 200, + }, + }); + }); + + it('ignores xhr that have not completed yet', function () { + const data = { + ...DEFAULT_DATA, + endTimestamp: undefined, + }; + + expect(handleXhr(data)).toEqual(null); + }); + + it('passes request/response size through if available', function () { + const data: HandlerDataXhr = { + ...DEFAULT_DATA, + xhr: { + ...DEFAULT_DATA.xhr, + __sentry_xhr__: { + ...DEFAULT_DATA.xhr.__sentry_xhr__, + request_body_size: 123, + response_body_size: 456, + }, + }, + }; + + expect(handleXhr(data)?.data).toEqual({ + method: 'GET', + statusCode: 200, + requestBodySize: 123, + responseBodySize: 456, + }); + }); +}); diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts index 0ddc6a9615f2..35b37f868d85 100644 --- a/packages/types/src/client.ts +++ b/packages/types/src/client.ts @@ -1,3 +1,4 @@ +import type { Breadcrumb, BreadcrumbHint } from './breadcrumb'; import type { EventDropReason } from './clientreport'; import type { DataCategory } from './datacategory'; import type { DsnComponents } from './dsn'; @@ -171,6 +172,11 @@ export interface Client { callback: (event: Event, sendResponse: TransportMakeRequestResponse | void) => void, ): void; + /** + * Register a callback before a breadcrumb is added. + */ + on?(hook: 'beforeAddBreadcrumb', callback: (breadcrumb: Breadcrumb, hint?: BreadcrumbHint) => void): void; + /** * Fire a hook event for transaction start and finish. Expects to be given a transaction as the * second argument. @@ -188,4 +194,9 @@ export interface Client { * second argument. */ emit?(hook: 'afterSendEvent', event: Event, sendResponse: TransportMakeRequestResponse | void): void; + + /** + * Fire a hook for when a bredacrumb is added. Expects the breadcrumb as second argument. + */ + emit?(hook: 'beforeAddBreadcrumb', breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void; } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 30df53a682d5..6cb59b9931f8 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -94,6 +94,6 @@ export type { export type { User, UserFeedback } from './user'; export type { WrappedFunction } from './wrappedfunction'; export type { Instrumenter } from './instrumenter'; -export type { HandlerDataFetch, SentryWrappedXMLHttpRequest } from './instrument'; +export type { HandlerDataFetch, HandlerDataXhr, SentryXhrData, SentryWrappedXMLHttpRequest } from './instrument'; export type { BrowserClientReplayOptions } from './browseroptions'; diff --git a/packages/types/src/instrument.ts b/packages/types/src/instrument.ts index 102376aa0a8b..94b4767fd875 100644 --- a/packages/types/src/instrument.ts +++ b/packages/types/src/instrument.ts @@ -4,24 +4,38 @@ type XHRSendInput = unknown; export interface SentryWrappedXMLHttpRequest { - [key: string]: any; - __sentry_xhr__?: { - method?: string; - url?: string; - status_code?: number; - body?: XHRSendInput; - }; + __sentry_xhr__?: SentryXhrData; + __sentry_own_request__?: boolean; +} + +export interface SentryXhrData { + method?: string; + url?: string; + status_code?: number; + body?: XHRSendInput; + request_body_size?: number; + response_body_size?: number; +} + +export interface HandlerDataXhr { + args: [string, string]; + xhr: SentryWrappedXMLHttpRequest; + startTimestamp?: number; + endTimestamp?: number; } interface SentryFetchData { method: string; url: string; + request_body_size?: number; + response_body_size?: number; } export interface HandlerDataFetch { args: any[]; fetchData: SentryFetchData; startTimestamp: number; + endTimestamp?: number; // This is actually `Response`, make sure to cast this where needed (not available in Node) response?: unknown; } diff --git a/packages/utils/src/instrument.ts b/packages/utils/src/instrument.ts index 6adeb72d64c0..e227016e1b6f 100644 --- a/packages/utils/src/instrument.ts +++ b/packages/utils/src/instrument.ts @@ -1,7 +1,13 @@ /* eslint-disable max-lines */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/ban-types */ -import type { HandlerDataFetch, SentryWrappedXMLHttpRequest, WrappedFunction } from '@sentry/types'; +import type { + HandlerDataFetch, + HandlerDataXhr, + SentryWrappedXMLHttpRequest, + SentryXhrData, + WrappedFunction, +} from '@sentry/types'; import { isInstanceOf, isString } from './is'; import { CONSOLE_LEVELS, logger } from './logger'; @@ -209,10 +215,8 @@ function instrumentXHR(): void { fill(xhrproto, 'open', function (originalOpen: () => void): () => void { return function (this: XMLHttpRequest & SentryWrappedXMLHttpRequest, ...args: any[]): void { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const xhr = this; const url = args[1]; - const xhrInfo: SentryWrappedXMLHttpRequest['__sentry_xhr__'] = (xhr.__sentry_xhr__ = { + const xhrInfo: SentryXhrData = (this.__sentry_xhr__ = { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access method: isString(args[0]) ? args[0].toUpperCase() : args[0], url: args[1], @@ -221,40 +225,47 @@ function instrumentXHR(): void { // if Sentry key appears in URL, don't capture it as a request // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (isString(url) && xhrInfo.method === 'POST' && url.match(/sentry_key/)) { - xhr.__sentry_own_request__ = true; + this.__sentry_own_request__ = true; } - const onreadystatechangeHandler = function (): void { - if (xhr.readyState === 4) { + const onreadystatechangeHandler: () => void = () => { + // For whatever reason, this is not the same instance here as from the outer method + const xhrInfo = this.__sentry_xhr__; + + if (!xhrInfo) { + return; + } + + if (this.readyState === 4) { try { // touching statusCode in some platforms throws // an exception - xhrInfo.status_code = xhr.status; + xhrInfo.status_code = this.status; } catch (e) { /* do nothing */ } triggerHandlers('xhr', { - args, + args: args as [string, string], endTimestamp: Date.now(), startTimestamp: Date.now(), - xhr, - }); + xhr: this, + } as HandlerDataXhr); } }; - if ('onreadystatechange' in xhr && typeof xhr.onreadystatechange === 'function') { - fill(xhr, 'onreadystatechange', function (original: WrappedFunction): Function { - return function (...readyStateArgs: any[]): void { + if ('onreadystatechange' in this && typeof this.onreadystatechange === 'function') { + fill(this, 'onreadystatechange', function (original: WrappedFunction): Function { + return function (this: SentryWrappedXMLHttpRequest, ...readyStateArgs: any[]): void { onreadystatechangeHandler(); - return original.apply(xhr, readyStateArgs); + return original.apply(this, readyStateArgs); }; }); } else { - xhr.addEventListener('readystatechange', onreadystatechangeHandler); + this.addEventListener('readystatechange', onreadystatechangeHandler); } - return originalOpen.apply(xhr, args); + return originalOpen.apply(this, args); }; }); diff --git a/scripts/node-unit-tests.ts b/scripts/node-unit-tests.ts index ebe252a95240..e5efc7c2ebd3 100644 --- a/scripts/node-unit-tests.ts +++ b/scripts/node-unit-tests.ts @@ -16,7 +16,13 @@ const DEFAULT_SKIP_TESTS_PACKAGES = [ ]; // These packages don't support Node 8 for syntax or dependency reasons. -const NODE_8_SKIP_TESTS_PACKAGES = ['@sentry/gatsby', '@sentry/serverless', '@sentry/nextjs', '@sentry/remix', '@sentry/sveltekit']; +const NODE_8_SKIP_TESTS_PACKAGES = [ + '@sentry/gatsby', + '@sentry/serverless', + '@sentry/nextjs', + '@sentry/remix', + '@sentry/sveltekit', +]; // We have to downgrade some of our dependencies in order to run tests in Node 8 and 10. const NODE_8_LEGACY_DEPENDENCIES = [ @@ -36,7 +42,6 @@ const NODE_12_LEGACY_DEPENDENCIES = ['lerna@3.13.4']; const NODE_14_SKIP_TESTS_PACKAGES = ['@sentry/sveltekit']; - type JSONValue = string | number | boolean | null | JSONArray | JSONObject; type JSONObject = { From ad32f947f77541a376e7ad0b08b148d35a6a1128 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 16 Mar 2023 16:01:32 +0100 Subject: [PATCH 29/54] test(tracing): Check if `beforeNavigate` does not change source (#7473) --- .../tracing-internal/test/browser/browsertracing.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/tracing-internal/test/browser/browsertracing.test.ts b/packages/tracing-internal/test/browser/browsertracing.test.ts index 94abe88e6cbb..e599648c2930 100644 --- a/packages/tracing-internal/test/browser/browsertracing.test.ts +++ b/packages/tracing-internal/test/browser/browsertracing.test.ts @@ -308,18 +308,20 @@ describe('BrowserTracing', () => { expect(mockBeforeNavigation).toHaveBeenCalledTimes(1); }); - it('sets transaction name source to default `custom` if name is not changed', () => { + it('sets transaction name source to default `url` if name is not changed', () => { const mockBeforeNavigation = jest.fn(ctx => ({ ...ctx, })); createBrowserTracing(true, { beforeNavigate: mockBeforeNavigation, - routingInstrumentation: customInstrumentRouting, + routingInstrumentation: (customStartTransaction: (obj: any) => void) => { + customStartTransaction({ name: 'a/path', op: 'pageload', metadata: { source: 'url' } }); + }, }); const transaction = getActiveTransaction(hub) as IdleTransaction; expect(transaction).toBeDefined(); expect(transaction.name).toBe('a/path'); - expect(transaction.metadata.source).toBe('custom'); + expect(transaction.metadata.source).toBe('url'); expect(mockBeforeNavigation).toHaveBeenCalledTimes(1); }); From be563613dc6c95dafc3576a633a8234d46817528 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 16 Mar 2023 17:37:43 +0100 Subject: [PATCH 30/54] fix(sveltekit): Make `handleErrorWithSentry` parameter optional in function declaration (#7487) --- packages/sveltekit/src/index.types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sveltekit/src/index.types.ts b/packages/sveltekit/src/index.types.ts index f332a526019f..5835b6863b61 100644 --- a/packages/sveltekit/src/index.types.ts +++ b/packages/sveltekit/src/index.types.ts @@ -18,7 +18,7 @@ import type * as serverSdk from './server'; export declare function init(options: Options | clientSdk.BrowserOptions | serverSdk.NodeOptions): void; export declare function handleErrorWithSentry( - handleError: T, + handleError?: T, ): ReturnType; export declare function wrapLoadWithSentry(origLoad: S): S; From af0224a18a341b028362705354f783cb79557576 Mon Sep 17 00:00:00 2001 From: Jonas Date: Thu, 16 Mar 2023 13:33:29 -0400 Subject: [PATCH 31/54] ref(utils): Limit max stack lines we parse (#7410) --- packages/utils/src/stacktrace.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/utils/src/stacktrace.ts b/packages/utils/src/stacktrace.ts index 535e6b12108b..f26c4c11a084 100644 --- a/packages/utils/src/stacktrace.ts +++ b/packages/utils/src/stacktrace.ts @@ -1,6 +1,6 @@ import type { StackFrame, StackLineParser, StackLineParserFn, StackParser } from '@sentry/types'; -const STACKTRACE_LIMIT = 50; +const STACKTRACE_FRAME_LIMIT = 50; // Used to sanitize webpack (error: *) wrapped stack errors const WEBPACK_ERROR_REGEXP = /\(error: (.*)\)/; @@ -16,7 +16,10 @@ export function createStackParser(...parsers: StackLineParser[]): StackParser { return (stack: string, skipFirst: number = 0): StackFrame[] => { const frames: StackFrame[] = []; - for (const line of stack.split('\n').slice(skipFirst)) { + 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: @@ -37,6 +40,10 @@ export function createStackParser(...parsers: StackLineParser[]): StackParser { break; } } + + if (frames.length >= STACKTRACE_FRAME_LIMIT) { + break; + } } return stripSentryFramesAndReverse(frames); @@ -67,7 +74,7 @@ export function stripSentryFramesAndReverse(stack: ReadonlyArray): S return []; } - const localStack = stack.slice(0, STACKTRACE_LIMIT); + const localStack = stack.slice(0, STACKTRACE_FRAME_LIMIT); const lastFrameFunction = localStack[localStack.length - 1].function; // If stack starts with one of our API calls, remove it (starts, meaning it's the top of the stack - aka last call) From 873d9bba69389d2e7155277d6c39ce5095af2a30 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 17 Mar 2023 08:44:12 +0100 Subject: [PATCH 32/54] ref: Use `Date.now()` instead of `new Date().getTime()` (#7492) This is slightly shorter and should actually be slightly more performant. --- packages/browser/test/integration/polyfills/raf.js | 2 +- packages/integrations/test/offline.test.ts | 2 +- packages/replay/src/coreHandlers/handleHistory.ts | 2 +- packages/replay/src/replay.ts | 12 ++++++------ packages/replay/src/session/Session.ts | 2 +- packages/replay/src/util/addEvent.ts | 2 +- packages/replay/src/util/addMemoryEntry.ts | 2 +- packages/replay/src/util/createBreadcrumb.ts | 2 +- packages/replay/test/fixtures/error.ts | 2 +- packages/replay/test/fixtures/transaction.ts | 2 +- packages/replay/test/mocks/mockRrweb.ts | 2 +- .../test/unit/coreHandlers/handleScope.test.ts | 2 +- .../replay/test/unit/session/getSession.test.ts | 14 +++++++------- packages/svelte/test/performance.test.ts | 4 ++-- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/browser/test/integration/polyfills/raf.js b/packages/browser/test/integration/polyfills/raf.js index 1d6bc9935c65..2b84451134d2 100644 --- a/packages/browser/test/integration/polyfills/raf.js +++ b/packages/browser/test/integration/polyfills/raf.js @@ -17,7 +17,7 @@ if (!window.requestAnimationFrame) window.requestAnimationFrame = function (callback, _element) { - var currTime = new Date().getTime(); + var currTime = Date.now(); var timeToCall = Math.max(0, 16 - (currTime - lastTime)); var id = window.setTimeout(function () { callback(currTime + timeToCall); diff --git a/packages/integrations/test/offline.test.ts b/packages/integrations/test/offline.test.ts index 624c166763b8..1dac96bc1e53 100644 --- a/packages/integrations/test/offline.test.ts +++ b/packages/integrations/test/offline.test.ts @@ -194,7 +194,7 @@ function prepopulateEvents(count: number = 1): void { for (let i = 0; i < count; i++) { events.push({ message: 'There was an error!', - timestamp: new Date().getTime(), + timestamp: Date.now(), }); } } diff --git a/packages/replay/src/coreHandlers/handleHistory.ts b/packages/replay/src/coreHandlers/handleHistory.ts index 4732c0118831..f75998689c01 100644 --- a/packages/replay/src/coreHandlers/handleHistory.ts +++ b/packages/replay/src/coreHandlers/handleHistory.ts @@ -9,7 +9,7 @@ interface HistoryHandlerData { function handleHistory(handlerData: HistoryHandlerData): ReplayPerformanceEntry { const { from, to } = handlerData; - const now = new Date().getTime() / 1000; + const now = Date.now() / 1000; return { type: 'navigation.push', diff --git a/packages/replay/src/replay.ts b/packages/replay/src/replay.ts index c42a5cc5b9b6..df2f3e6c9774 100644 --- a/packages/replay/src/replay.ts +++ b/packages/replay/src/replay.ts @@ -78,7 +78,7 @@ export class ReplayContainer implements ReplayContainerInterface { /** * Timestamp of the last user activity. This lives across sessions. */ - private _lastActivity: number = new Date().getTime(); + private _lastActivity: number = Date.now(); /** * Is the integration currently active? @@ -108,7 +108,7 @@ export class ReplayContainer implements ReplayContainerInterface { traceIds: new Set(), urls: [], earliestEvent: null, - initialTimestamp: new Date().getTime(), + initialTimestamp: Date.now(), initialUrl: '', }; @@ -442,7 +442,7 @@ export class ReplayContainer implements ReplayContainerInterface { this._clearContext(); this._context.initialUrl = url; - this._context.initialTimestamp = new Date().getTime(); + this._context.initialTimestamp = Date.now(); this._context.urls.push(url); } @@ -634,14 +634,14 @@ export class ReplayContainer implements ReplayContainerInterface { /** * Update user activity (across session lifespans) */ - private _updateUserActivity(_lastActivity: number = new Date().getTime()): void { + private _updateUserActivity(_lastActivity: number = Date.now()): void { this._lastActivity = _lastActivity; } /** * Updates the session's last activity timestamp */ - private _updateSessionActivity(_lastActivity: number = new Date().getTime()): void { + private _updateSessionActivity(_lastActivity: number = Date.now()): void { if (this.session) { this.session.lastActivity = _lastActivity; this._maybeSaveSession(); @@ -768,7 +768,7 @@ export class ReplayContainer implements ReplayContainerInterface { eventContext, session: this.session, options: this.getOptions(), - timestamp: new Date().getTime(), + timestamp: Date.now(), }); } catch (err) { this._handleException(err); diff --git a/packages/replay/src/session/Session.ts b/packages/replay/src/session/Session.ts index d20fa0daf33c..9089ea54c76c 100644 --- a/packages/replay/src/session/Session.ts +++ b/packages/replay/src/session/Session.ts @@ -7,7 +7,7 @@ import { isSampled } from '../util/isSampled'; * Get a session with defaults & applied sampling. */ export function makeSession(session: Partial & { sampled: Sampled }): Session { - const now = new Date().getTime(); + const now = Date.now(); const id = session.id || uuid4(); // Note that this means we cannot set a started/lastActivity of `0`, but this should not be relevant outside of tests. const started = session.started || now; diff --git a/packages/replay/src/util/addEvent.ts b/packages/replay/src/util/addEvent.ts index 357ecaca0e61..b32050665519 100644 --- a/packages/replay/src/util/addEvent.ts +++ b/packages/replay/src/util/addEvent.ts @@ -31,7 +31,7 @@ export async function addEvent( // page has been left open and idle for a long period of time and user // comes back to trigger a new session. The performance entries rely on // `performance.timeOrigin`, which is when the page first opened. - if (timestampInMs + replay.timeouts.sessionIdle < new Date().getTime()) { + if (timestampInMs + replay.timeouts.sessionIdle < Date.now()) { return null; } diff --git a/packages/replay/src/util/addMemoryEntry.ts b/packages/replay/src/util/addMemoryEntry.ts index 8d31e06cd9d9..92431e45f2d4 100644 --- a/packages/replay/src/util/addMemoryEntry.ts +++ b/packages/replay/src/util/addMemoryEntry.ts @@ -33,7 +33,7 @@ function createMemoryEntry(memoryEntry: MemoryInfo): ReplayMemoryEntry { const { jsHeapSizeLimit, totalJSHeapSize, usedJSHeapSize } = memoryEntry; // we don't want to use `getAbsoluteTime` because it adds the event time to the // time origin, so we get the current timestamp instead - const time = new Date().getTime() / 1000; + const time = Date.now() / 1000; return { type: 'memory', name: 'memory', diff --git a/packages/replay/src/util/createBreadcrumb.ts b/packages/replay/src/util/createBreadcrumb.ts index b9f7527b0180..b8ff6097d571 100644 --- a/packages/replay/src/util/createBreadcrumb.ts +++ b/packages/replay/src/util/createBreadcrumb.ts @@ -9,7 +9,7 @@ export function createBreadcrumb( breadcrumb: Pick & Partial>, ): Breadcrumb { return { - timestamp: new Date().getTime() / 1000, + timestamp: Date.now() / 1000, type: 'default', ...breadcrumb, }; diff --git a/packages/replay/test/fixtures/error.ts b/packages/replay/test/fixtures/error.ts index 6008f81209e1..046efa619539 100644 --- a/packages/replay/test/fixtures/error.ts +++ b/packages/replay/test/fixtures/error.ts @@ -1,7 +1,7 @@ import type { Event } from '@sentry/types'; export function Error(obj?: Event): any { - const timestamp = new Date().getTime() / 1000; + const timestamp = Date.now() / 1000; return { exception: { diff --git a/packages/replay/test/fixtures/transaction.ts b/packages/replay/test/fixtures/transaction.ts index 1e8ec7a3272a..9362749e7c3b 100644 --- a/packages/replay/test/fixtures/transaction.ts +++ b/packages/replay/test/fixtures/transaction.ts @@ -1,7 +1,7 @@ import type { Event, SeverityLevel } from '@sentry/types'; export function Transaction(traceId?: string, obj?: Partial): any { - const timestamp = new Date().getTime() / 1000; + const timestamp = Date.now() / 1000; return { contexts: { diff --git a/packages/replay/test/mocks/mockRrweb.ts b/packages/replay/test/mocks/mockRrweb.ts index cef95093d125..0c7ef971483d 100644 --- a/packages/replay/test/mocks/mockRrweb.ts +++ b/packages/replay/test/mocks/mockRrweb.ts @@ -19,7 +19,7 @@ export type RecordMock = jest.MockedFunction & RecordAdditio function createCheckoutPayload(isCheckout: boolean = true) { return { data: { isCheckout }, - timestamp: new Date().getTime(), + timestamp: Date.now(), type: isCheckout ? 2 : 3, }; } diff --git a/packages/replay/test/unit/coreHandlers/handleScope.test.ts b/packages/replay/test/unit/coreHandlers/handleScope.test.ts index c7ba7cdc75ce..2f46fafba635 100644 --- a/packages/replay/test/unit/coreHandlers/handleScope.test.ts +++ b/packages/replay/test/unit/coreHandlers/handleScope.test.ts @@ -24,7 +24,7 @@ describe('Unit | coreHandlers | handleScope', () => { } const testMsg = { - timestamp: new Date().getTime() / 1000, + timestamp: Date.now() / 1000, message: 'testing', category: 'console', }; diff --git a/packages/replay/test/unit/session/getSession.test.ts b/packages/replay/test/unit/session/getSession.test.ts index 4ac92b148e75..2e7d3ec969b7 100644 --- a/packages/replay/test/unit/session/getSession.test.ts +++ b/packages/replay/test/unit/session/getSession.test.ts @@ -17,7 +17,7 @@ const SAMPLE_RATES = { errorSampleRate: 0, }; -function createMockSession(when: number = new Date().getTime()) { +function createMockSession(when: number = Date.now()) { return makeSession({ id: 'test_session_id', segmentId: 0, @@ -66,7 +66,7 @@ describe('Unit | session | getSession', () => { }); it('creates a non-sticky session, regardless of session existing in sessionStorage', function () { - saveSession(createMockSession(new Date().getTime() - 10000)); + saveSession(createMockSession(Date.now() - 10000)); const { session } = getSession({ timeouts: { @@ -93,8 +93,8 @@ describe('Unit | session | getSession', () => { ...SAMPLE_RATES, currentSession: makeSession({ id: 'old_session_id', - lastActivity: new Date().getTime() - 1001, - started: new Date().getTime() - 1001, + lastActivity: Date.now() - 1001, + started: Date.now() - 1001, segmentId: 0, sampled: 'session', }), @@ -142,7 +142,7 @@ describe('Unit | session | getSession', () => { }); it('fetches an existing sticky session', function () { - const now = new Date().getTime(); + const now = Date.now(); saveSession(createMockSession(now)); const { session } = getSession({ @@ -168,8 +168,8 @@ describe('Unit | session | getSession', () => { }); it('fetches an expired sticky session', function () { - const now = new Date().getTime(); - saveSession(createMockSession(new Date().getTime() - 2000)); + const now = Date.now(); + saveSession(createMockSession(Date.now() - 2000)); const { session } = getSession({ timeouts: { diff --git a/packages/svelte/test/performance.test.ts b/packages/svelte/test/performance.test.ts index cbfd320f35bd..1e02eb549d5f 100644 --- a/packages/svelte/test/performance.test.ts +++ b/packages/svelte/test/performance.test.ts @@ -80,7 +80,7 @@ describe('Sentry.trackComponent()', () => { it('creates an update span, when the component is updated', async () => { // Make the finish() function actually end the initSpan testInitSpan.finish.mockImplementation(() => { - testInitSpan.endTimestamp = new Date().getTime(); + testInitSpan.endTimestamp = Date.now(); }); // first we create the component @@ -171,7 +171,7 @@ describe('Sentry.trackComponent()', () => { it("doesn't record update spans, if there's no ongoing transaction at that time", async () => { // Make the finish() function actually end the initSpan testInitSpan.finish.mockImplementation(() => { - testInitSpan.endTimestamp = new Date().getTime(); + testInitSpan.endTimestamp = Date.now(); }); // first we create the component From 5f6e5b5632e49d0b1bd2a1a61818e8bac630d8a4 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 17 Mar 2023 09:17:50 +0100 Subject: [PATCH 33/54] chore(sveltekit): Update `README` with setup instructions (#7490) --- packages/sveltekit/README.md | 103 ++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/packages/sveltekit/README.md b/packages/sveltekit/README.md index bb0206a72ac4..b1a8dbc3e943 100644 --- a/packages/sveltekit/README.md +++ b/packages/sveltekit/README.md @@ -29,4 +29,105 @@ Currently, the minimum supported version of SvelteKit is `1.0.0`. This package is a wrapper around `@sentry/node` for the server and `@sentry/svelte` for the client, with added functionality related to SvelteKit. -TODO: Add usage instructions +## Usage + +Although the SDK is not yet stable, you're more than welcome to give it a try and provide us with early feedback. + +**Here's how to get started:** + +1. Ensure you've set up the [`@sveltejs/adapter-node` adapter](https://kit.svelte.dev/docs/adapter-node) + +2. Install the Sentry SvelteKit SDK: + + ```bash + # Using npm + npm install @sentry/sveltekit + + # Using yarn + yarn add @sentry/sveltekit + ``` + +3. Create a `sentry.client.config.(js|ts)` file in the root directory of your SvelteKit project. + In this file you can configure the client-side Sentry SDK just like every other browser-based SDK: + + ```javascript + import * as Sentry from '@sentry/sveltekit'; + + Sentry.init({ + dsn: 'https://27dbca58093d401f9b18f88c40ec718f@o447951.ingest.sentry.io/4504796902588416', + + // For instance, initialize Session Replay: + replaysSessionSampleRate: 0.1, + replaysOnErrorSampleRate: 1.0, + integrations: [new Sentry.Replay()] + }); + ``` + +4. Add our `withSentryViteConfig` wrapper around your Vite config so that the Sentry SDK is added to your application in `vite.config.(js|ts)`: + ```javascript + import { sveltekit } from '@sveltejs/kit/vite'; + import { withSentryViteConfig } from '@sentry/sveltekit'; + + export default withSentryViteConfig({ + plugins: [sveltekit()], + // ... + }); + ``` + +5. Create a `sentry.server.config.(js|ts)` file in the root directory of your SvelteKit project. + In this file you can configure the server-side Sentry SDK, like the Sentry Node SDK: + + ```javascript + import * as Sentry from '@sentry/sveltekit'; + + Sentry.init({ + dsn: 'https://27dbca58093d401f9b18f88c40ec718f@o447951.ingest.sentry.io/4504796902588416', + }); + ``` + +6. To catch errors in your `load` functions in `+page.(js|ts)`, wrap our `wrapLoadWithSentry` function: + + ```javascript + import { wrapLoadWithSentry } from '@sentry/sveltekit'; + + export const load = wrapLoadWithSentry((event) => { + //... + }); + ``` + +7. In your `hooks.client.(js|ts)` or `hooks.server.(js|ts)`, you can wrap the `handleError` function as follows: + + ```javascript + import { handleErrorWithSentry } from '@sentry/sveltekit'; + import type { HandleClientError } from '@sveltejs/kit'; + + const myErrorHandler = ((input) => { + console.log('This is the client error handler'); + console.log(input.error); + }) satisfies HandleClientError; + + export const handleError = handleErrorWithSentry(myErrorHandler); + + // or alternatively, if you don't have a custom error handler: + // export const handleError = handleErrorWithSentry(); + ``` + +## Known Limitations + +This SDK is still under active development and several features are missing. +Take a look at our [SvelteKit SDK Development Roadmap](https://github.com/getsentry/sentry-javascript/issues/6692) to follow the progress: + +- **Performance monitoring** is not yet fully supported. + You can add the `BrowserTracing` integration but proper instrumentation for routes, page loads and navigations is still missing. + This will be addressed next, as we release the next alpha builds. + +- **Source Maps** upload is not yet working correctly. + We already investigated [some options](https://github.com/getsentry/sentry-javascript/discussions/5838#discussioncomment-4696985) but uploading source maps doesn't work automtatically out of the box yet. + This will be addressed next, as we release the next alpha builds. + +- **Adapters** other than `@sveltejs/adapter-node` are currently not supported. + We haven't yet tested other platforms like Vercel. + This is on our roadmap but it will come at a later time. + +- We're aiming to **simplify SDK setup** in the future so that you don't have to go in and manually add our wrappers to all your `load` functions and hooks. + This will be addressed once the SDK supports all Sentry features. From 90bd190bf499e8c686e51c75142dc1f6033afdfa Mon Sep 17 00:00:00 2001 From: baked Date: Fri, 17 Mar 2023 11:00:16 +0100 Subject: [PATCH 34/54] fix(nextjs): Fix runtime error for static pages (#7476) --- .../templates/serverComponentWrapperTemplate.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts index 1c937a5f355c..7a8e755a147a 100644 --- a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts +++ b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts @@ -38,9 +38,15 @@ if (typeof serverComponent === 'function') { // If we call the headers function inside the build phase, Next.js will automatically mark the server component as // dynamic(SSR) which we do not want in case the users have a static component. if (process.env.NEXT_PHASE !== 'phase-production-build') { - const headersList = headers(); - sentryTraceHeader = headersList.get('sentry-trace'); - baggageHeader = headersList.get('baggage'); + // try/catch because calling headers() when a previously statically generated page is being revalidated causes a + // runtime error in next.js as switching a page from static to dynamic during runtime is not allowed + try { + const headersList = headers(); + sentryTraceHeader = headersList.get('sentry-trace'); + baggageHeader = headersList.get('baggage'); + } catch { + /** empty */ + } } return Sentry.wrapServerComponentWithSentry(originalFunction, { From 3045390311c4f6831732338ad85c102d26eb84ed Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Fri, 17 Mar 2023 10:09:12 +0000 Subject: [PATCH 35/54] feat(browser): Export `BrowserTracing` from `@sentry/browser` (#7472) --- packages/browser/package.json | 1 + packages/browser/src/index.ts | 4 ++++ packages/tracing-internal/src/browser/browsertracing.ts | 4 +++- rollup/bundleHelpers.js | 6 ++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/browser/package.json b/packages/browser/package.json index fb2a3ccd2a54..a4d9d54bb414 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -20,6 +20,7 @@ "@sentry/replay": "7.43.0", "@sentry/types": "7.43.0", "@sentry/utils": "7.43.0", + "@sentry-internal/tracing": "7.43.0", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 5714a6f680d6..44f3e11dcfa4 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -29,6 +29,10 @@ export { INTEGRATIONS as Integrations }; export { Replay } from '@sentry/replay'; // __ROLLUP_EXCLUDE_REPLAY_FROM_BUNDLES_END__ +// __ROLLUP_EXCLUDE_BROWSER_TRACING_FROM_BUNDLES_BEGIN__ +export { BrowserTracing } from '@sentry-internal/tracing'; +// __ROLLUP_EXCLUDE_BROWSER_TRACING_FROM_BUNDLES_END__ + // __ROLLUP_EXCLUDE_OFFLINE_FROM_BUNDLES_BEGIN__ export { makeBrowserOfflineTransport } from './transports/offline'; // __ROLLUP_EXCLUDE_OFFLINE_FROM_BUNDLES_END__ diff --git a/packages/tracing-internal/src/browser/browsertracing.ts b/packages/tracing-internal/src/browser/browsertracing.ts index 3575c0a20e91..99ce53dd022b 100644 --- a/packages/tracing-internal/src/browser/browsertracing.ts +++ b/packages/tracing-internal/src/browser/browsertracing.ts @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ import type { Hub, IdleTransaction } from '@sentry/core'; -import { extractTraceparentData, startIdleTransaction, TRACING_DEFAULTS } from '@sentry/core'; +import { addTracingExtensions, extractTraceparentData, startIdleTransaction, TRACING_DEFAULTS } from '@sentry/core'; import type { EventProcessor, Integration, Transaction, TransactionContext, TransactionSource } from '@sentry/types'; import { baggageHeaderToDynamicSamplingContext, getDomElement, logger } from '@sentry/utils'; @@ -172,6 +172,8 @@ export class BrowserTracing implements Integration { private _collectWebVitals: () => void; public constructor(_options?: Partial) { + addTracingExtensions(); + this.options = { ...DEFAULT_BROWSER_TRACING_OPTIONS, ..._options, diff --git a/rollup/bundleHelpers.js b/rollup/bundleHelpers.js index e81069990fa4..6266717aec0b 100644 --- a/rollup/bundleHelpers.js +++ b/rollup/bundleHelpers.js @@ -35,6 +35,7 @@ export function makeBaseBundleConfig(options) { includeReplay, includeOffline, includeBrowserProfiling, + includeBrowserTracing, } = options; const nodeResolvePlugin = makeNodeResolvePlugin(); @@ -47,6 +48,7 @@ export function makeBaseBundleConfig(options) { const excludeReplayShimPlugin = makeExcludeBlockPlugin('REPLAY_SHIM'); const excludeOfflineTransport = makeExcludeBlockPlugin('OFFLINE'); const excludeBrowserProfiling = makeExcludeBlockPlugin('BROWSER_PROFILING'); + const excludeBrowserTracing = makeExcludeBlockPlugin('BROWSER_TRACING'); const replayShimPlugin = makeReplayShimPlugin(); // The `commonjs` plugin is the `esModuleInterop` of the bundling world. When used with `transformMixedEsModules`, it @@ -82,6 +84,10 @@ export function makeBaseBundleConfig(options) { standAloneBundleConfig.plugins.push(excludeBrowserProfiling); } + if (!includeBrowserTracing) { + standAloneBundleConfig.plugins.push(excludeBrowserTracing); + } + // used by `@sentry/integrations` and `@sentry/wasm` (bundles which need to be combined with a stand-alone SDK bundle) const addOnBundleConfig = { // These output settings are designed to mimic an IIFE. We don't use Rollup's `iife` format because we don't want to From 128f7bcd435958dc3e8f1b954df8ce1186baddc6 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Fri, 17 Mar 2023 10:48:40 +0000 Subject: [PATCH 36/54] feat(tracing): Move `registerErrorInstrumentation` to `@sentry/core` (#7494) --- .../src => core/src/tracing}/errors.ts | 12 ++++++++++-- packages/core/src/tracing/hubextensions.ts | 3 +++ .../test => core/test/lib/tracing}/errors.test.ts | 4 ++-- packages/tracing-internal/src/extensions.ts | 5 ----- 4 files changed, 15 insertions(+), 9 deletions(-) rename packages/{tracing-internal/src => core/src/tracing}/errors.ts (77%) rename packages/{tracing-internal/test => core/test/lib/tracing}/errors.test.ts (94%) diff --git a/packages/tracing-internal/src/errors.ts b/packages/core/src/tracing/errors.ts similarity index 77% rename from packages/tracing-internal/src/errors.ts rename to packages/core/src/tracing/errors.ts index 3d41e43830f0..3351f428fecf 100644 --- a/packages/tracing-internal/src/errors.ts +++ b/packages/core/src/tracing/errors.ts @@ -1,11 +1,19 @@ -import type { SpanStatusType } from '@sentry/core'; -import { getActiveTransaction } from '@sentry/core'; import { addInstrumentationHandler, logger } from '@sentry/utils'; +import type { SpanStatusType } from './span'; +import { getActiveTransaction } from './utils'; + +let errorsInstrumented = false; + /** * Configures global error listeners */ export function registerErrorInstrumentation(): void { + if (errorsInstrumented) { + return; + } + + errorsInstrumented = true; addInstrumentationHandler('error', errorCallback); addInstrumentationHandler('unhandledrejection', errorCallback); } diff --git a/packages/core/src/tracing/hubextensions.ts b/packages/core/src/tracing/hubextensions.ts index 9624df4b709e..b5518f95e04b 100644 --- a/packages/core/src/tracing/hubextensions.ts +++ b/packages/core/src/tracing/hubextensions.ts @@ -4,6 +4,7 @@ import { isNaN, logger } from '@sentry/utils'; import type { Hub } from '../hub'; import { getMainCarrier } from '../hub'; import { hasTracingEnabled } from '../utils/hasTracingEnabled'; +import { registerErrorInstrumentation } from './errors'; import { IdleTransaction } from './idletransaction'; import { Transaction } from './transaction'; @@ -237,4 +238,6 @@ export function addTracingExtensions(): void { if (!carrier.__SENTRY__.extensions.traceHeaders) { carrier.__SENTRY__.extensions.traceHeaders = traceHeaders; } + + registerErrorInstrumentation(); } diff --git a/packages/tracing-internal/test/errors.test.ts b/packages/core/test/lib/tracing/errors.test.ts similarity index 94% rename from packages/tracing-internal/test/errors.test.ts rename to packages/core/test/lib/tracing/errors.test.ts index 1dba00b825b1..8bf12f675032 100644 --- a/packages/tracing-internal/test/errors.test.ts +++ b/packages/core/test/lib/tracing/errors.test.ts @@ -2,8 +2,8 @@ import { BrowserClient } from '@sentry/browser'; import { addTracingExtensions, Hub, makeMain } from '@sentry/core'; import type { InstrumentHandlerCallback, InstrumentHandlerType } from '@sentry/utils'; -import { getDefaultBrowserClientOptions } from '../../tracing/test/testutils'; -import { registerErrorInstrumentation } from '../src/errors'; +import { getDefaultBrowserClientOptions } from '../../../../tracing/test/testutils'; +import { registerErrorInstrumentation } from '../../../src/tracing/errors'; const mockAddInstrumentationHandler = jest.fn(); let mockErrorCallback: InstrumentHandlerCallback = () => undefined; diff --git a/packages/tracing-internal/src/extensions.ts b/packages/tracing-internal/src/extensions.ts index e3c2ab511804..555a1451b55e 100644 --- a/packages/tracing-internal/src/extensions.ts +++ b/packages/tracing-internal/src/extensions.ts @@ -2,8 +2,6 @@ import { addTracingExtensions, getMainCarrier } from '@sentry/core'; import type { Integration, IntegrationClass } from '@sentry/types'; import { dynamicRequire, isNodeEnv, loadModule } from '@sentry/utils'; -import { registerErrorInstrumentation } from './errors'; - /** * @private */ @@ -66,7 +64,4 @@ export function addExtensionMethods(): void { if (isNodeEnv()) { _autoloadDatabaseIntegrations(); } - - // If an error happens globally, we should make sure transaction status is set to error. - registerErrorInstrumentation(); } From c2717673e3a50d557d4a267b6155d0e344de1799 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Fri, 17 Mar 2023 11:58:20 +0000 Subject: [PATCH 37/54] feat(browser): Re-export `addTracingExtensions` from `@sentry/core` (#7498) --- packages/browser/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 44f3e11dcfa4..010ef61e79df 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -31,6 +31,7 @@ export { Replay } from '@sentry/replay'; // __ROLLUP_EXCLUDE_BROWSER_TRACING_FROM_BUNDLES_BEGIN__ export { BrowserTracing } from '@sentry-internal/tracing'; +export { addTracingExtensions } from '@sentry/core'; // __ROLLUP_EXCLUDE_BROWSER_TRACING_FROM_BUNDLES_END__ // __ROLLUP_EXCLUDE_OFFLINE_FROM_BUNDLES_BEGIN__ From 901acb92e59a98cc2677ec4bdb6f2763ee191b83 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 17 Mar 2023 13:37:37 +0100 Subject: [PATCH 38/54] test(replay): Skip flaky test in firefox (#7496) --- .../suites/replay/errors/errorMode/test.ts | 5 +++-- .../suites/replay/errors/errorsInSession/test.ts | 9 +++++---- .../suites/replay/requests/test.ts | 12 ++++++------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts b/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts index 826e04effc32..cc17fa80cb9c 100644 --- a/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts +++ b/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts @@ -17,8 +17,9 @@ import { sentryTest( '[error-mode] should start recording and switch to session mode once an error is thrown', - async ({ getLocalTestPath, page }) => { - if (shouldSkipReplayTest()) { + async ({ getLocalTestPath, page, browserName }) => { + // This was sometimes flaky on firefox/webkit, so skipping for now + if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts b/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts index 3badb2b956cb..01962b6667e5 100644 --- a/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts +++ b/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts @@ -13,8 +13,8 @@ import { sentryTest( '[session-mode] replay event should contain an error id of an error that occurred during session recording', async ({ getLocalTestPath, page, browserName, forceFlushReplay }) => { - // TODO(replay): This is flakey on firefox where clicks are flakey - if (shouldSkipReplayTest() || ['firefox'].includes(browserName)) { + // Skipping this in firefox/webkit because it is flakey there + if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { sentryTest.skip(); } @@ -86,8 +86,9 @@ sentryTest( sentryTest( '[session-mode] replay event should not contain an error id of a dropped error while recording', - async ({ getLocalTestPath, page, forceFlushReplay }) => { - if (shouldSkipReplayTest()) { + async ({ getLocalTestPath, page, forceFlushReplay, browserName }) => { + // Skipping this in firefox/webkit because it is flakey there + if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/requests/test.ts b/packages/browser-integration-tests/suites/replay/requests/test.ts index 3b711afbfe88..de879a920b0e 100644 --- a/packages/browser-integration-tests/suites/replay/requests/test.ts +++ b/packages/browser-integration-tests/suites/replay/requests/test.ts @@ -5,10 +5,10 @@ import { expectedFetchPerformanceSpan, expectedXHRPerformanceSpan } from '../../ import { getReplayRecordingContent, shouldSkipReplayTest, waitForReplayRequest } from '../../../utils/replayHelpers'; sentryTest('replay recording should contain fetch request span', async ({ getLocalTestPath, page, browserName }) => { - // For some reason, observing and waiting for requests in firefox is extremely flaky. - // We therefore skip this test for firefox and only test on chromium/webkit. + // For some reason, observing and waiting for requests in firefox/webkit is extremely flaky. + // We therefore skip this test for firefox and only test on chromium. // Possibly related: https://github.com/microsoft/playwright/issues/11390 - if (shouldSkipReplayTest() || browserName === 'firefox') { + if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { sentryTest.skip(); } @@ -48,9 +48,9 @@ sentryTest('replay recording should contain fetch request span', async ({ getLoc }); sentryTest('replay recording should contain XHR request span', async ({ getLocalTestPath, page, browserName }) => { - // For some reason, observing and waiting for requests in firefox is extremely flaky. - // We therefore skip this test for firefox and only test on chromium/webkit. - if (shouldSkipReplayTest() || browserName === 'firefox') { + // For some reason, observing and waiting for requests in firefox/webkit is extremely flaky. + // We therefore skip this test for firefox and only test on chromium. + if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { sentryTest.skip(); } From 6a2fdacc220c5b3d66388032aca3aa071b662b4f Mon Sep 17 00:00:00 2001 From: uchanlee Date: Fri, 17 Mar 2023 22:14:37 +0900 Subject: [PATCH 39/54] test(node): fix cookie test description typo (#7501) --- packages/node/test/requestdata.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node/test/requestdata.test.ts b/packages/node/test/requestdata.test.ts index 744cc9077c37..e5acf69c3297 100644 --- a/packages/node/test/requestdata.test.ts +++ b/packages/node/test/requestdata.test.ts @@ -310,7 +310,7 @@ describe.each([oldExtractRequestData, newExtractRequestData])( }); }); - it('includes the `Cookie` header in requestdata.headers, if `cookies` is not set in the options', () => { + it('includes the `Cookie` header in requestdata.headers, if `cookies` is set in the options', () => { const mockReq = { cookies: { foo: 'bar' }, headers: { cookie: 'foo=bar', otherHeader: 'hello' }, From 4a6256e4cd68c2f72beb8a9e1cb339be1d31060d Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 17 Mar 2023 14:26:28 +0100 Subject: [PATCH 40/54] fix(replay): Ensure network breadcrumbs have all data (#7491) --- .../suites/replay/requests/subject.js | 2 +- .../utils/replayEventTemplates.ts | 15 +- .../browser/src/integrations/breadcrumbs.ts | 73 ++-- .../src/coreHandlers/addNetworkBreadcrumb.ts | 26 ++ .../replay/src/coreHandlers/handleFetch.ts | 19 +- ...dcrumbs.ts => handleNetworkBreadcrumbs.ts} | 137 +++++-- packages/replay/src/coreHandlers/handleXhr.ts | 19 +- .../replay/src/util/addGlobalListeners.ts | 9 +- .../extendNetworkBreadcrumbs.test.ts | 63 ---- .../handleNetworkBreadcrumbs.test.ts | 340 ++++++++++++++++++ packages/types/src/breadcrumb.ts | 31 ++ packages/types/src/index.ts | 9 +- 12 files changed, 576 insertions(+), 167 deletions(-) create mode 100644 packages/replay/src/coreHandlers/addNetworkBreadcrumb.ts rename packages/replay/src/coreHandlers/{extendNetworkBreadcrumbs.ts => handleNetworkBreadcrumbs.ts} (50%) delete mode 100644 packages/replay/test/unit/coreHandlers/extendNetworkBreadcrumbs.test.ts create mode 100644 packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts diff --git a/packages/browser-integration-tests/suites/replay/requests/subject.js b/packages/browser-integration-tests/suites/replay/requests/subject.js index 7e5b305eabc3..a58f304fc687 100644 --- a/packages/browser-integration-tests/suites/replay/requests/subject.js +++ b/packages/browser-integration-tests/suites/replay/requests/subject.js @@ -6,7 +6,7 @@ document.getElementById('go-background').addEventListener('click', () => { }); document.getElementById('fetch').addEventListener('click', () => { - fetch('https://example.com'); + fetch('https://example.com', { method: 'POST', body: 'foo' }); }); document.getElementById('xhr').addEventListener('click', () => { diff --git a/packages/browser-integration-tests/utils/replayEventTemplates.ts b/packages/browser-integration-tests/utils/replayEventTemplates.ts index c417624f9b9a..8891f5aaeb6d 100644 --- a/packages/browser-integration-tests/utils/replayEventTemplates.ts +++ b/packages/browser-integration-tests/utils/replayEventTemplates.ts @@ -132,23 +132,26 @@ export const expectedFPPerformanceSpan = { export const expectedFetchPerformanceSpan = { op: 'resource.fetch', - description: expect.any(String), + description: 'https://example.com', startTimestamp: expect.any(Number), endTimestamp: expect.any(Number), data: { - method: expect.any(String), - statusCode: expect.any(Number), + method: 'POST', + statusCode: 200, + responseBodySize: 11, + requestBodySize: 3, }, }; export const expectedXHRPerformanceSpan = { op: 'resource.xhr', - description: expect.any(String), + description: 'https://example.com', startTimestamp: expect.any(Number), endTimestamp: expect.any(Number), data: { - method: expect.any(String), - statusCode: expect.any(Number), + method: 'GET', + statusCode: 200, + responseBodySize: 11, }, }; diff --git a/packages/browser/src/integrations/breadcrumbs.ts b/packages/browser/src/integrations/breadcrumbs.ts index ce79b89ea78d..cfd833ef759a 100644 --- a/packages/browser/src/integrations/breadcrumbs.ts +++ b/packages/browser/src/integrations/breadcrumbs.ts @@ -2,6 +2,12 @@ /* eslint-disable max-lines */ import { getCurrentHub } from '@sentry/core'; import type { Event as SentryEvent, HandlerDataFetch, HandlerDataXhr, Integration } from '@sentry/types'; +import type { + FetchBreadcrumbData, + FetchBreadcrumbHint, + XhrBreadcrumbData, + XhrBreadcrumbHint, +} from '@sentry/types/build/types/breadcrumb'; import { addInstrumentationHandler, getEventDescription, @@ -217,27 +223,35 @@ function _consoleBreadcrumb(handlerData: HandlerData & { args: unknown[]; level: * Creates breadcrumbs from XHR API calls */ function _xhrBreadcrumb(handlerData: HandlerData & HandlerDataXhr): void { + const { startTimestamp, endTimestamp } = handlerData; + // We only capture complete, non-sentry requests - if (!handlerData.endTimestamp || !handlerData.xhr.__sentry_xhr__) { + if (!startTimestamp || !endTimestamp || !handlerData.xhr.__sentry_xhr__) { return; } const { method, url, status_code, body } = handlerData.xhr.__sentry_xhr__; + const data: XhrBreadcrumbData = { + method, + url, + status_code, + }; + + const hint: XhrBreadcrumbHint = { + xhr: handlerData.xhr, + input: body, + startTimestamp, + endTimestamp, + }; + getCurrentHub().addBreadcrumb( { category: 'xhr', - data: { - method, - url, - status_code, - }, + data, type: 'http', }, - { - xhr: handlerData.xhr, - input: body, - }, + hint, ); } @@ -245,8 +259,10 @@ function _xhrBreadcrumb(handlerData: HandlerData & HandlerDataXhr): void { * Creates breadcrumbs from fetch API calls */ function _fetchBreadcrumb(handlerData: HandlerData & HandlerDataFetch & { response?: Response }): void { + const { startTimestamp, endTimestamp } = handlerData; + // We only capture complete fetch requests - if (!handlerData.endTimestamp) { + if (!endTimestamp) { return; } @@ -256,32 +272,41 @@ function _fetchBreadcrumb(handlerData: HandlerData & HandlerDataFetch & { respon } if (handlerData.error) { + const data: FetchBreadcrumbData = handlerData.fetchData; + const hint: FetchBreadcrumbHint = { + data: handlerData.error, + input: handlerData.args, + startTimestamp, + endTimestamp, + }; + getCurrentHub().addBreadcrumb( { category: 'fetch', - data: handlerData.fetchData, + data, level: 'error', type: 'http', }, - { - data: handlerData.error, - input: handlerData.args, - }, + hint, ); } else { + const data: FetchBreadcrumbData = { + ...handlerData.fetchData, + status_code: handlerData.response && handlerData.response.status, + }; + const hint: FetchBreadcrumbHint = { + input: handlerData.args, + response: handlerData.response, + startTimestamp, + endTimestamp, + }; getCurrentHub().addBreadcrumb( { category: 'fetch', - data: { - ...handlerData.fetchData, - status_code: handlerData.response && handlerData.response.status, - }, + data, type: 'http', }, - { - input: handlerData.args, - response: handlerData.response, - }, + hint, ); } } diff --git a/packages/replay/src/coreHandlers/addNetworkBreadcrumb.ts b/packages/replay/src/coreHandlers/addNetworkBreadcrumb.ts new file mode 100644 index 000000000000..19f5a17a1c9f --- /dev/null +++ b/packages/replay/src/coreHandlers/addNetworkBreadcrumb.ts @@ -0,0 +1,26 @@ +import type { ReplayContainer, ReplayPerformanceEntry } from '../types'; +import { createPerformanceSpans } from '../util/createPerformanceSpans'; +import { shouldFilterRequest } from '../util/shouldFilterRequest'; + +/** Add a performance entry breadcrumb */ +export function addNetworkBreadcrumb(replay: ReplayContainer, result: ReplayPerformanceEntry | null): void { + if (!replay.isEnabled()) { + return; + } + + if (result === null) { + return; + } + + if (shouldFilterRequest(replay, result.name)) { + return; + } + + replay.addUpdate(() => { + createPerformanceSpans(replay, [result]); + // Returning true will cause `addUpdate` to not flush + // We do not want network requests to cause a flush. This will prevent + // recurring/polling requests from keeping the replay session alive. + return true; + }); +} diff --git a/packages/replay/src/coreHandlers/handleFetch.ts b/packages/replay/src/coreHandlers/handleFetch.ts index 4ba163d5c5c9..2569706d7c33 100644 --- a/packages/replay/src/coreHandlers/handleFetch.ts +++ b/packages/replay/src/coreHandlers/handleFetch.ts @@ -1,8 +1,7 @@ import type { HandlerDataFetch } from '@sentry/types'; import type { ReplayContainer, ReplayPerformanceEntry } from '../types'; -import { createPerformanceSpans } from '../util/createPerformanceSpans'; -import { shouldFilterRequest } from '../util/shouldFilterRequest'; +import { addNetworkBreadcrumb } from './addNetworkBreadcrumb'; /** only exported for tests */ export function handleFetch(handlerData: HandlerDataFetch): null | ReplayPerformanceEntry { @@ -39,20 +38,6 @@ export function handleFetchSpanListener(replay: ReplayContainer): (handlerData: const result = handleFetch(handlerData); - if (result === null) { - return; - } - - if (shouldFilterRequest(replay, result.name)) { - return; - } - - replay.addUpdate(() => { - createPerformanceSpans(replay, [result]); - // Returning true will cause `addUpdate` to not flush - // We do not want network requests to cause a flush. This will prevent - // recurring/polling requests from keeping the replay session alive. - return true; - }); + addNetworkBreadcrumb(replay, result); }; } diff --git a/packages/replay/src/coreHandlers/extendNetworkBreadcrumbs.ts b/packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts similarity index 50% rename from packages/replay/src/coreHandlers/extendNetworkBreadcrumbs.ts rename to packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts index bc1acb1c7c72..0ee72f9edc9c 100644 --- a/packages/replay/src/coreHandlers/extendNetworkBreadcrumbs.ts +++ b/packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts @@ -2,15 +2,31 @@ import { getCurrentHub } from '@sentry/core'; import type { Breadcrumb, BreadcrumbHint, + FetchBreadcrumbData, + FetchBreadcrumbHint, HandlerDataFetch, SentryWrappedXMLHttpRequest, TextEncoderInternal, + XhrBreadcrumbData, + XhrBreadcrumbHint, } from '@sentry/types'; -import { logger } from '@sentry/utils'; +import { addInstrumentationHandler, logger } from '@sentry/utils'; + +import type { ReplayContainer, ReplayPerformanceEntry } from '../types'; +import { addNetworkBreadcrumb } from './addNetworkBreadcrumb'; +import { handleFetchSpanListener } from './handleFetch'; +import { handleXhrSpanListener } from './handleXhr'; type RequestBody = null | Blob | BufferSource | FormData | URLSearchParams | string; +type XhrHint = XhrBreadcrumbHint & { xhr: XMLHttpRequest & SentryWrappedXMLHttpRequest; input?: RequestBody }; +type FetchHint = FetchBreadcrumbHint & { + input: HandlerDataFetch['args']; + response: Response; +}; + interface ExtendedNetworkBreadcrumbsOptions { + replay: ReplayContainer; textEncoder: TextEncoderInternal; } @@ -23,25 +39,31 @@ interface ExtendedNetworkBreadcrumbsOptions { * * to the breadcrumb data. */ -export function extendNetworkBreadcrumbs(): void { +export function handleNetworkBreadcrumbs(replay: ReplayContainer): void { const client = getCurrentHub().getClient(); try { const textEncoder = new TextEncoder(); const options: ExtendedNetworkBreadcrumbsOptions = { + replay, textEncoder, }; if (client && client.on) { - client.on('beforeAddBreadcrumb', (breadcrumb, hint) => _beforeNetworkBreadcrumb(options, breadcrumb, hint)); + client.on('beforeAddBreadcrumb', (breadcrumb, hint) => handleNetworkBreadcrumb(options, breadcrumb, hint)); + } else { + // Fallback behavior + addInstrumentationHandler('fetch', handleFetchSpanListener(replay)); + addInstrumentationHandler('xhr', handleXhrSpanListener(replay)); } } catch { // Do nothing } } -function _beforeNetworkBreadcrumb( +/** just exported for tests */ +export function handleNetworkBreadcrumb( options: ExtendedNetworkBreadcrumbsOptions, breadcrumb: Breadcrumb, hint?: BreadcrumbHint, @@ -51,40 +73,80 @@ function _beforeNetworkBreadcrumb( } try { - if (breadcrumb.category === 'xhr' && hint && hint.xhr) { - _enrichXhrBreadcrumb( - breadcrumb as Breadcrumb & { data: object }, - { - xhr: hint.xhr as XMLHttpRequest & SentryWrappedXMLHttpRequest, - body: hint.input as RequestBody, - }, - options, - ); + if (_isXhrBreadcrumb(breadcrumb) && _isXhrHint(hint)) { + // Enriches the breadcrumb overall + _enrichXhrBreadcrumb(breadcrumb, hint, options); + + // Create a replay performance entry from this breadcrumb + const result = _makeNetworkReplayBreadcrumb('resource.xhr', breadcrumb, hint); + addNetworkBreadcrumb(options.replay, result); } - if (breadcrumb.category === 'fetch' && hint) { - _enrichFetchBreadcrumb( - breadcrumb as Breadcrumb & { data: object }, - { - input: hint.input as HandlerDataFetch['args'], - response: hint.response as Response, - }, - options, - ); + if (_isFetchBreadcrumb(breadcrumb) && _isFetchHint(hint)) { + // Enriches the breadcrumb overall + _enrichFetchBreadcrumb(breadcrumb, hint, options); + + // Create a replay performance entry from this breadcrumb + const result = _makeNetworkReplayBreadcrumb('resource.fetch', breadcrumb, hint); + addNetworkBreadcrumb(options.replay, result); } } catch (e) { __DEBUG_BUILD__ && logger.warn('Error when enriching network breadcrumb'); } } +function _makeNetworkReplayBreadcrumb( + type: string, + breadcrumb: Breadcrumb & { data: FetchBreadcrumbData | XhrBreadcrumbData }, + hint: FetchBreadcrumbHint | XhrBreadcrumbHint, +): ReplayPerformanceEntry | null { + const { startTimestamp, endTimestamp } = hint; + + if (!endTimestamp) { + return null; + } + + const { + url, + method, + status_code: statusCode, + request_body_size: requestBodySize, + response_body_size: responseBodySize, + } = breadcrumb.data; + + if (url === undefined) { + return null; + } + + const result: ReplayPerformanceEntry & { data: object } = { + type, + start: startTimestamp / 1000, + end: endTimestamp / 1000, + name: url, + data: { + method, + statusCode, + }, + }; + + if (requestBodySize) { + result.data.requestBodySize = requestBodySize; + } + if (responseBodySize) { + result.data.responseBodySize = responseBodySize; + } + + return result; +} + function _enrichXhrBreadcrumb( - breadcrumb: Breadcrumb & { data: object }, - hint: { xhr: XMLHttpRequest & SentryWrappedXMLHttpRequest; body?: RequestBody }, + breadcrumb: Breadcrumb & { data: XhrBreadcrumbData }, + hint: XhrHint, options: ExtendedNetworkBreadcrumbsOptions, ): void { - const { xhr, body } = hint; + const { xhr, input } = hint; - const reqSize = getBodySize(body, options.textEncoder); + const reqSize = getBodySize(input, options.textEncoder); const resSize = xhr.getResponseHeader('content-length') ? parseContentSizeHeader(xhr.getResponseHeader('content-length')) : getBodySize(xhr.response, options.textEncoder); @@ -98,11 +160,8 @@ function _enrichXhrBreadcrumb( } function _enrichFetchBreadcrumb( - breadcrumb: Breadcrumb & { data: object }, - hint: { - input: HandlerDataFetch['args']; - response: Response; - }, + breadcrumb: Breadcrumb & { data: FetchBreadcrumbData }, + hint: FetchHint, options: ExtendedNetworkBreadcrumbsOptions, ): void { const { input, response } = hint; @@ -179,3 +238,19 @@ function getFetchBody(fetchArgs: unknown[] = []): RequestInit['body'] | undefine return (fetchArgs[1] as RequestInit).body; } + +function _isXhrBreadcrumb(breadcrumb: Breadcrumb): breadcrumb is Breadcrumb & { data: XhrBreadcrumbData } { + return breadcrumb.category === 'xhr'; +} + +function _isFetchBreadcrumb(breadcrumb: Breadcrumb): breadcrumb is Breadcrumb & { data: FetchBreadcrumbData } { + return breadcrumb.category === 'fetch'; +} + +function _isXhrHint(hint?: BreadcrumbHint): hint is XhrHint { + return hint && hint.xhr; +} + +function _isFetchHint(hint?: BreadcrumbHint): hint is FetchHint { + return hint && hint.response; +} diff --git a/packages/replay/src/coreHandlers/handleXhr.ts b/packages/replay/src/coreHandlers/handleXhr.ts index 1e39ec409ea8..f78c41fc831e 100644 --- a/packages/replay/src/coreHandlers/handleXhr.ts +++ b/packages/replay/src/coreHandlers/handleXhr.ts @@ -1,8 +1,7 @@ import type { HandlerDataXhr } from '@sentry/types'; import type { ReplayContainer, ReplayPerformanceEntry } from '../types'; -import { createPerformanceSpans } from '../util/createPerformanceSpans'; -import { shouldFilterRequest } from '../util/shouldFilterRequest'; +import { addNetworkBreadcrumb } from './addNetworkBreadcrumb'; /** only exported for tests */ export function handleXhr(handlerData: HandlerDataXhr): ReplayPerformanceEntry | null { @@ -49,20 +48,6 @@ export function handleXhrSpanListener(replay: ReplayContainer): (handlerData: Ha const result = handleXhr(handlerData); - if (result === null) { - return; - } - - if (shouldFilterRequest(replay, result.name)) { - return; - } - - replay.addUpdate(() => { - createPerformanceSpans(replay, [result]); - // Returning true will cause `addUpdate` to not flush - // We do not want network requests to cause a flush. This will prevent - // recurring/polling requests from keeping the replay session alive. - return true; - }); + addNetworkBreadcrumb(replay, result); }; } diff --git a/packages/replay/src/util/addGlobalListeners.ts b/packages/replay/src/util/addGlobalListeners.ts index ea685c49241a..fc68f322d090 100644 --- a/packages/replay/src/util/addGlobalListeners.ts +++ b/packages/replay/src/util/addGlobalListeners.ts @@ -2,14 +2,12 @@ import type { BaseClient } from '@sentry/core'; import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core'; import { addInstrumentationHandler } from '@sentry/utils'; -import { extendNetworkBreadcrumbs } from '../coreHandlers/extendNetworkBreadcrumbs'; import { handleAfterSendEvent } from '../coreHandlers/handleAfterSendEvent'; import { handleDomListener } from '../coreHandlers/handleDom'; -import { handleFetchSpanListener } from '../coreHandlers/handleFetch'; import { handleGlobalEventListener } from '../coreHandlers/handleGlobalEvent'; import { handleHistorySpanListener } from '../coreHandlers/handleHistory'; +import { handleNetworkBreadcrumbs } from '../coreHandlers/handleNetworkBreadcrumbs'; import { handleScopeListener } from '../coreHandlers/handleScope'; -import { handleXhrSpanListener } from '../coreHandlers/handleXhr'; import type { ReplayContainer } from '../types'; /** @@ -24,9 +22,8 @@ export function addGlobalListeners(replay: ReplayContainer): void { scope.addScopeListener(handleScopeListener(replay)); } addInstrumentationHandler('dom', handleDomListener(replay)); - addInstrumentationHandler('fetch', handleFetchSpanListener(replay)); - addInstrumentationHandler('xhr', handleXhrSpanListener(replay)); addInstrumentationHandler('history', handleHistorySpanListener(replay)); + handleNetworkBreadcrumbs(replay); // If a custom client has no hooks yet, we continue to use the "old" implementation const hasHooks = !!(client && client.on); @@ -39,6 +36,4 @@ export function addGlobalListeners(replay: ReplayContainer): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any (client as BaseClient).on('afterSendEvent', handleAfterSendEvent(replay)); } - - extendNetworkBreadcrumbs(); } diff --git a/packages/replay/test/unit/coreHandlers/extendNetworkBreadcrumbs.test.ts b/packages/replay/test/unit/coreHandlers/extendNetworkBreadcrumbs.test.ts deleted file mode 100644 index a8d6885d43c2..000000000000 --- a/packages/replay/test/unit/coreHandlers/extendNetworkBreadcrumbs.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { TextEncoder } from 'util'; - -import { getBodySize, parseContentSizeHeader } from '../../../src/coreHandlers/extendNetworkBreadcrumbs'; - -describe('Unit | coreHandlers | extendNetworkBreadcrumbs', () => { - describe('parseContentSizeHeader()', () => { - it.each([ - [undefined, undefined], - [null, undefined], - ['', undefined], - ['12', 12], - ['abc', undefined], - ])('works with %s header value', (headerValue, size) => { - expect(parseContentSizeHeader(headerValue)).toBe(size); - }); - }); - - describe('getBodySize()', () => { - const textEncoder = new TextEncoder(); - - it('works with empty body', () => { - expect(getBodySize(undefined, textEncoder)).toBe(undefined); - expect(getBodySize(null, textEncoder)).toBe(undefined); - expect(getBodySize('', textEncoder)).toBe(undefined); - }); - - it('works with string body', () => { - expect(getBodySize('abcd', textEncoder)).toBe(4); - // Emojis are correctly counted as mutliple characters - expect(getBodySize('With emoji: 😈', textEncoder)).toBe(16); - }); - - it('works with URLSearchParams', () => { - const params = new URLSearchParams(); - params.append('name', 'Jane'); - params.append('age', '42'); - params.append('emoji', '😈'); - - expect(getBodySize(params, textEncoder)).toBe(35); - }); - - it('works with FormData', () => { - const formData = new FormData(); - formData.append('name', 'Jane'); - formData.append('age', '42'); - formData.append('emoji', '😈'); - - expect(getBodySize(formData, textEncoder)).toBe(35); - }); - - it('works with Blob', () => { - const blob = new Blob(['Hello world: 😈'], { type: 'text/html' }); - - expect(getBodySize(blob, textEncoder)).toBe(30); - }); - - it('works with ArrayBuffer', () => { - const arrayBuffer = new ArrayBuffer(8); - - expect(getBodySize(arrayBuffer, textEncoder)).toBe(8); - }); - }); -}); diff --git a/packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts b/packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts new file mode 100644 index 000000000000..d0a7b5cedca1 --- /dev/null +++ b/packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts @@ -0,0 +1,340 @@ +import type { + Breadcrumb, + BreadcrumbHint, + FetchBreadcrumbHint, + TextEncoderInternal, + XhrBreadcrumbHint, +} from '@sentry/types'; +import { TextEncoder } from 'util'; + +import { BASE_TIMESTAMP } from '../..'; +import { + getBodySize, + handleNetworkBreadcrumb, + parseContentSizeHeader, +} from '../../../src/coreHandlers/handleNetworkBreadcrumbs'; +import type { EventBufferArray } from '../../../src/eventBuffer/EventBufferArray'; +import type { ReplayContainer } from '../../../src/types'; +import { setupReplayContainer } from '../../utils/setupReplayContainer'; + +jest.useFakeTimers(); + +describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => { + describe('parseContentSizeHeader()', () => { + it.each([ + [undefined, undefined], + [null, undefined], + ['', undefined], + ['12', 12], + ['abc', undefined], + ])('works with %s header value', (headerValue, size) => { + expect(parseContentSizeHeader(headerValue)).toBe(size); + }); + }); + + describe('getBodySize()', () => { + const textEncoder = new TextEncoder(); + + it('works with empty body', () => { + expect(getBodySize(undefined, textEncoder)).toBe(undefined); + expect(getBodySize(null, textEncoder)).toBe(undefined); + expect(getBodySize('', textEncoder)).toBe(undefined); + }); + + it('works with string body', () => { + expect(getBodySize('abcd', textEncoder)).toBe(4); + // Emojis are correctly counted as mutliple characters + expect(getBodySize('With emoji: 😈', textEncoder)).toBe(16); + }); + + it('works with URLSearchParams', () => { + const params = new URLSearchParams(); + params.append('name', 'Jane'); + params.append('age', '42'); + params.append('emoji', '😈'); + + expect(getBodySize(params, textEncoder)).toBe(35); + }); + + it('works with FormData', () => { + const formData = new FormData(); + formData.append('name', 'Jane'); + formData.append('age', '42'); + formData.append('emoji', '😈'); + + expect(getBodySize(formData, textEncoder)).toBe(35); + }); + + it('works with Blob', () => { + const blob = new Blob(['Hello world: 😈'], { type: 'text/html' }); + + expect(getBodySize(blob, textEncoder)).toBe(30); + }); + + it('works with ArrayBuffer', () => { + const arrayBuffer = new ArrayBuffer(8); + + expect(getBodySize(arrayBuffer, textEncoder)).toBe(8); + }); + }); + + describe('handleNetworkBreadcrumb()', () => { + let options: { + replay: ReplayContainer; + textEncoder: TextEncoderInternal; + }; + + beforeEach(() => { + jest.setSystemTime(BASE_TIMESTAMP); + + options = { + textEncoder: new TextEncoder(), + replay: setupReplayContainer(), + }; + + jest.runAllTimers(); + }); + + it('ignores breadcrumb without data', () => { + const breadcrumb: Breadcrumb = {}; + const hint: BreadcrumbHint = {}; + handleNetworkBreadcrumb(options, breadcrumb, hint); + + expect(breadcrumb).toEqual({}); + expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([]); + }); + + it('ignores non-network breadcrumbs', () => { + const breadcrumb: Breadcrumb = { + category: 'foo', + data: {}, + }; + const hint: BreadcrumbHint = {}; + handleNetworkBreadcrumb(options, breadcrumb, hint); + + expect(breadcrumb).toEqual({ + category: 'foo', + data: {}, + }); + expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([]); + }); + + it('handles full xhr breadcrumb', async () => { + const breadcrumb: Breadcrumb = { + category: 'xhr', + data: { + method: 'GET', + url: 'https://example.com', + status_code: 200, + }, + }; + const xhr = new XMLHttpRequest(); + Object.defineProperty(xhr, 'response', { + value: 'test response', + }); + const hint: XhrBreadcrumbHint = { + xhr, + input: 'test input', + startTimestamp: BASE_TIMESTAMP + 1000, + endTimestamp: BASE_TIMESTAMP + 2000, + }; + handleNetworkBreadcrumb(options, breadcrumb, hint); + + expect(breadcrumb).toEqual({ + category: 'xhr', + data: { + method: 'GET', + request_body_size: 10, + response_body_size: 13, + status_code: 200, + url: 'https://example.com', + }, + }); + + jest.runAllTimers(); + + expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([ + { + type: 5, + timestamp: (BASE_TIMESTAMP + 1000) / 1000, + data: { + tag: 'performanceSpan', + payload: { + data: { + method: 'GET', + requestBodySize: 10, + responseBodySize: 13, + statusCode: 200, + }, + description: 'https://example.com', + endTimestamp: (BASE_TIMESTAMP + 2000) / 1000, + op: 'resource.xhr', + startTimestamp: (BASE_TIMESTAMP + 1000) / 1000, + }, + }, + }, + ]); + }); + + it('handles minimal xhr breadcrumb', async () => { + const breadcrumb: Breadcrumb = { + category: 'xhr', + data: { + url: 'https://example.com', + status_code: 200, + }, + }; + const xhr = new XMLHttpRequest(); + + const hint: XhrBreadcrumbHint = { + xhr, + input: undefined, + startTimestamp: BASE_TIMESTAMP + 1000, + endTimestamp: BASE_TIMESTAMP + 2000, + }; + handleNetworkBreadcrumb(options, breadcrumb, hint); + + expect(breadcrumb).toEqual({ + category: 'xhr', + data: { + status_code: 200, + url: 'https://example.com', + }, + }); + + jest.runAllTimers(); + + expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([ + { + type: 5, + timestamp: (BASE_TIMESTAMP + 1000) / 1000, + data: { + tag: 'performanceSpan', + payload: { + data: { + statusCode: 200, + }, + description: 'https://example.com', + endTimestamp: (BASE_TIMESTAMP + 2000) / 1000, + op: 'resource.xhr', + startTimestamp: (BASE_TIMESTAMP + 1000) / 1000, + }, + }, + }, + ]); + }); + + it('handles full fetch breadcrumb', async () => { + const breadcrumb: Breadcrumb = { + category: 'fetch', + data: { + method: 'GET', + url: 'https://example.com', + status_code: 200, + }, + }; + + const mockResponse = { + headers: { + get: () => '13', + }, + } as unknown as Response; + + const hint: FetchBreadcrumbHint = { + input: ['GET', { body: 'test input' }], + response: mockResponse, + startTimestamp: BASE_TIMESTAMP + 1000, + endTimestamp: BASE_TIMESTAMP + 2000, + }; + handleNetworkBreadcrumb(options, breadcrumb, hint); + + expect(breadcrumb).toEqual({ + category: 'fetch', + data: { + method: 'GET', + request_body_size: 10, + response_body_size: 13, + status_code: 200, + url: 'https://example.com', + }, + }); + + jest.runAllTimers(); + + expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([ + { + type: 5, + timestamp: (BASE_TIMESTAMP + 1000) / 1000, + data: { + tag: 'performanceSpan', + payload: { + data: { + method: 'GET', + requestBodySize: 10, + responseBodySize: 13, + statusCode: 200, + }, + description: 'https://example.com', + endTimestamp: (BASE_TIMESTAMP + 2000) / 1000, + op: 'resource.fetch', + startTimestamp: (BASE_TIMESTAMP + 1000) / 1000, + }, + }, + }, + ]); + }); + + it('handles minimal fetch breadcrumb', async () => { + const breadcrumb: Breadcrumb = { + category: 'fetch', + data: { + url: 'https://example.com', + status_code: 200, + }, + }; + + const mockResponse = { + headers: { + get: () => '', + }, + } as unknown as Response; + + const hint: FetchBreadcrumbHint = { + input: [], + response: mockResponse, + startTimestamp: BASE_TIMESTAMP + 1000, + endTimestamp: BASE_TIMESTAMP + 2000, + }; + handleNetworkBreadcrumb(options, breadcrumb, hint); + + expect(breadcrumb).toEqual({ + category: 'fetch', + data: { + status_code: 200, + url: 'https://example.com', + }, + }); + + jest.runAllTimers(); + + expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([ + { + type: 5, + timestamp: (BASE_TIMESTAMP + 1000) / 1000, + data: { + tag: 'performanceSpan', + payload: { + data: { + statusCode: 200, + }, + description: 'https://example.com', + endTimestamp: (BASE_TIMESTAMP + 2000) / 1000, + op: 'resource.fetch', + startTimestamp: (BASE_TIMESTAMP + 1000) / 1000, + }, + }, + }, + ]); + }); + }); +}); diff --git a/packages/types/src/breadcrumb.ts b/packages/types/src/breadcrumb.ts index b8e2552a2f34..9f9b36bd6dcb 100644 --- a/packages/types/src/breadcrumb.ts +++ b/packages/types/src/breadcrumb.ts @@ -16,3 +16,34 @@ export interface Breadcrumb { export interface BreadcrumbHint { [key: string]: any; } + +export interface FetchBreadcrumbData { + method: string; + url: string; + status_code?: number; + request_body_size?: number; + response_body_size?: number; +} + +export interface XhrBreadcrumbData { + method?: string; + url?: string; + status_code?: number; + request_body_size?: number; + response_body_size?: number; +} + +export interface FetchBreadcrumbHint { + input: any[]; + data?: unknown; + response?: unknown; + startTimestamp: number; + endTimestamp: number; +} + +export interface XhrBreadcrumbHint { + xhr: unknown; + input: unknown; + startTimestamp: number; + endTimestamp: number; +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 6cb59b9931f8..e62fe6390ac8 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,5 +1,12 @@ export type { Attachment } from './attachment'; -export type { Breadcrumb, BreadcrumbHint } from './breadcrumb'; +export type { + Breadcrumb, + BreadcrumbHint, + FetchBreadcrumbData, + XhrBreadcrumbData, + FetchBreadcrumbHint, + XhrBreadcrumbHint, +} from './breadcrumb'; export type { Client } from './client'; export type { ClientReport, Outcome, EventDropReason } from './clientreport'; export type { Context, Contexts, DeviceContext, OsContext, AppContext, CultureContext, TraceContext } from './context'; From 30bba1f6c9370755709aef6488f0bcafe7ca1818 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 17 Mar 2023 14:27:52 +0100 Subject: [PATCH 41/54] chore(repo): Add `yarn yalc:publish` scripts (#7502) Add a new yarn script to all SDK packages: `yalc:publish`. This script will publish locally built SDK packages to a local [yalc](https://github.com/wclr/yalc) repository which we can use as an alternative to `yarn link` for using our SDKs locally in test projects. --- CONTRIBUTING.md | 9 +++++ docs/using-yalc.md | 51 ++++++++++++++++++++++++ package.json | 6 ++- packages/angular-ivy/package.json | 3 +- packages/angular/package.json | 3 +- packages/browser/package.json | 3 +- packages/core/package.json | 3 +- packages/gatsby/package.json | 3 +- packages/integrations/package.json | 3 +- packages/nextjs/package.json | 3 +- packages/node/package.json | 3 +- packages/opentelemetry-node/package.json | 3 +- packages/react/package.json | 3 +- packages/remix/package.json | 3 +- packages/replay/package.json | 3 +- packages/serverless/package.json | 3 +- packages/svelte/package.json | 3 +- packages/sveltekit/package.json | 3 +- packages/tracing-internal/package.json | 3 +- packages/tracing/package.json | 3 +- packages/types/package.json | 3 +- packages/utils/package.json | 3 +- packages/vue/package.json | 3 +- packages/wasm/package.json | 3 +- yarn.lock | 26 +++++++++++- 25 files changed, 131 insertions(+), 24 deletions(-) create mode 100644 docs/using-yalc.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9fd0522d8062..9048da5efddf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,6 +33,15 @@ Since we are using [`TypeScript`](https://www.typescriptlang.org/), you need to - `yarn build:dev:filter `, which runs `yarn build:dev` only in projects relevant to the given package (so, for example, running `yarn build:dev:filter @sentry/react` will build the `react` package, all of its dependencies (`utils`, `core`, `browser`, etc), and all packages which depend on it (currently `gatsby` and `nextjs`)) - `yarn build:dev:watch`, which runs `yarn build:dev` in watch mode (recommended) + +## Testing SDK Packages Locally + +To test local versions of SDK packages, for instance in test projects, you have a couple of options: + +* Use [`yarn link`](https://classic.yarnpkg.com/lang/en/docs/cli/link/) to symlink your package to the test project. +* Use [`yalc` to install SDK packages](./docs/using-yalc.md) as if they were already published. +* Run `build:tarball` in the repo and `yarn add ./path/to/tarball.tgz` in the project. + ## Adding Tests **Any nontrivial fixes/features should include tests.** You'll find a `test` folder in each package. diff --git a/docs/using-yalc.md b/docs/using-yalc.md new file mode 100644 index 000000000000..e75255a9cd72 --- /dev/null +++ b/docs/using-yalc.md @@ -0,0 +1,51 @@ +# Using `yalc` for Local SDK Testing + +[Yalc](https://github.com/wclr/yalc) is a simple local dependency repository which we can use to work with local versions of our SDKs. +This is a good alternative to `npm|yarn link` for packages where linking is problematic (e.g. SvelteKit or Angular). + +Here's how to set up and use yalc: + +## Installing `yalc` + +Either install yalc globally, + +```sh +npm install -g yalc + +yarn global add yalc +``` + +or add it to your desired test projects (same command without the `-g|global` flags) + +## Registering/Updating packages + +Whenever you want to make your local changes available to your test projects (e.g. after a local code change), run: + +```sh +yarn yalc:publish +``` + +If you run this command in the root of the repo, this will publish all SDK packages to the local yalc repo. If you run it in a specific SDK package, it will just publish this package. You **don't need to** call `yalc update` in your test project. Already linked test projects will be update automatically. + +## Using yalc packages + +In your test project, run + +```sh +yalc add @sentry/browser #or any other SDK package +``` + +to add the local SDK package to your project. + +**Important:** You need to `yalc add` the dependencies of the SDK package as well (e.g. core, utils, types, etc.). + +## Troubleshooting: + +### My changes are not applied to the test project + +Did you run `yarn build && yarn publish:yalc` after making your changes? + +### My test project uses Vite and I still don't see changes + +Vite pre-bundles and caches dependencies for dev builds. It [doesn't recognize changes in yalc packages though](https://github.com/wclr/yalc/issues/189) :( To make these changes show up anyway, run `vite dev --force`. + diff --git a/package.json b/package.json index 4b9b23b531ce..f2177c40bb71 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "test:unit": "lerna run --ignore @sentry-internal/* test:unit", "test-ci-browser": "lerna run test --ignore \"@sentry/{node,opentelemetry-node,serverless,nextjs,remix,gatsby}\" --ignore @sentry-internal/*", "test-ci-node": "ts-node ./scripts/node-unit-tests.ts", - "test:update-snapshots": "lerna run test:update-snapshots" + "test:update-snapshots": "lerna run test:update-snapshots", + "yalc:publish": "lerna run yalc:publish" }, "volta": { "node": "16.19.0", @@ -117,7 +118,8 @@ "tslib": "^2.3.1", "typedoc": "^0.18.0", "typescript": "3.8.3", - "vitest": "^0.29.2" + "vitest": "^0.29.2", + "yalc": "^1.0.0-pre.53" }, "resolutions": { "**/agent-base": "5" diff --git a/packages/angular-ivy/package.json b/packages/angular-ivy/package.json index 2e123b1536a5..b4b0bfd876b3 100644 --- a/packages/angular-ivy/package.json +++ b/packages/angular-ivy/package.json @@ -56,7 +56,8 @@ "fix:prettier": "prettier --write \"{src,test,scripts}/**/**.ts\"", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --format stylish", - "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"" + "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/angular/package.json b/packages/angular/package.json index 94222f6942f8..5106c688cdab 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -59,7 +59,8 @@ "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "yarn test:unit", "test:unit": "jest", - "test:unit:watch": "jest --watch" + "test:unit:watch": "jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/browser/package.json b/packages/browser/package.json index a4d9d54bb414..ed3970eae99a 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -75,7 +75,8 @@ "test:integration:checkbrowsers": "node scripts/checkbrowsers.js", "test:package": "node test/package/npm-build.js && rm test/package/tmp.js", "test:unit:watch": "jest --watch", - "test:integration:watch": "test/integration/run.js --watch" + "test:integration:watch": "test/integration/run.js --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts --bundles && yalc publish ./build/npm --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/core/package.json b/packages/core/package.json index e2852403a7e0..bc5eabc8d6f5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -40,7 +40,8 @@ "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "jest", "test:watch": "jest --watch", - "version": "node ../../scripts/versionbump.js src/version.ts" + "version": "node ../../scripts/versionbump.js src/version.ts", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index bc9d53348496..a5e39f833d69 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -56,7 +56,8 @@ "lint:eslint": "eslint . --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "yarn ts-node scripts/pretest.ts && yarn jest", - "test:watch": "yarn ts-node scripts/pretest.ts && yarn jest --watch" + "test:watch": "yarn ts-node scripts/pretest.ts && yarn jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 512e8b0557ea..8267b24ef897 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -45,7 +45,8 @@ "lint:eslint": "eslint . --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "jest", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts --bundles && yalc publish ./build/npm --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 227649b18e65..d3d0dbd95276 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -75,7 +75,8 @@ "test:types": "cd test/types && yarn test", "test:watch": "jest --watch", "vercel:branch": "source vercel/set-up-branch-for-test-app-use.sh", - "vercel:project": "source vercel/make-project-use-current-branch.sh" + "vercel:project": "source vercel/make-project-use-current-branch.sh", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/node/package.json b/packages/node/package.json index 832387bda88c..2c241aa4444c 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -56,7 +56,8 @@ "test:jest": "jest", "test:release-health": "node test/manual/release-health/runner.js", "test:webpack": "cd test/manual/webpack-domain/ && yarn --silent && node npm-build.js", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/opentelemetry-node/package.json b/packages/opentelemetry-node/package.json index ab4808ae0a57..d99bbe241585 100644 --- a/packages/opentelemetry-node/package.json +++ b/packages/opentelemetry-node/package.json @@ -55,7 +55,8 @@ "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "yarn test:jest", "test:jest": "jest", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/react/package.json b/packages/react/package.json index a98817cc203c..aeabc835fb28 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -69,7 +69,8 @@ "lint:eslint": "eslint . --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "jest", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/remix/package.json b/packages/remix/package.json index d8a3d9f03259..3c42ec1a82b8 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -70,7 +70,8 @@ "test:integration:client:ci": "yarn test:integration:client --browser='all' --reporter='line'", "test:integration:server": "export NODE_OPTIONS='--stack-trace-limit=25' && jest --config=test/integration/jest.config.js test/integration/test/server/", "test:unit": "jest", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/replay/package.json b/packages/replay/package.json index ccbbfb8bdef0..37b2b2233cbc 100644 --- a/packages/replay/package.json +++ b/packages/replay/package.json @@ -29,7 +29,8 @@ "test": "jest", "test:watch": "jest --watch", "bootstrap:demo": "cd demo && yarn", - "start:demo": "yarn build:dev && cd demo && yarn start" + "start:demo": "yarn build:dev && cd demo && yarn start", + "yalc:publish": "ts-node ../../scripts/prepack.ts --bundles && yalc publish ./build/npm --push" }, "repository": { "type": "git", diff --git a/packages/serverless/package.json b/packages/serverless/package.json index 887912408838..aad69db0ed07 100644 --- a/packages/serverless/package.json +++ b/packages/serverless/package.json @@ -57,7 +57,8 @@ "lint:eslint": "eslint . --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "jest", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts --bundles && yalc publish ./build/npm --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 8e1819bc17ed..ca893e3b9b47 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -49,7 +49,8 @@ "lint:eslint": "eslint . --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "jest", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index d9f9da77528d..f652768c6736 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -52,7 +52,8 @@ "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "yarn test:unit", "test:unit": "vitest run", - "test:watch": "vitest --watch" + "test:watch": "vitest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/tracing-internal/package.json b/packages/tracing-internal/package.json index a19bc69c6a80..141a27e33857 100644 --- a/packages/tracing-internal/package.json +++ b/packages/tracing-internal/package.json @@ -44,7 +44,8 @@ "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test:unit": "jest", "test": "jest", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 7bae1b2c2e3b..8d098d192b47 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -48,7 +48,8 @@ "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test:unit": "jest", "test": "jest", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts --bundles && yalc publish ./build/npm --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/types/package.json b/packages/types/package.json index c5caadd3e873..f472e1996755 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -31,7 +31,8 @@ "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "fix": "run-s fix:eslint fix:prettier", "fix:eslint": "eslint . --format stylish --fix", - "fix:prettier": "prettier --write \"{src,test,scripts}/**/**.ts\"" + "fix:prettier": "prettier --write \"{src,test,scripts}/**/**.ts\"", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/utils/package.json b/packages/utils/package.json index f0a5803d24fb..1425cdedc49f 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -45,7 +45,8 @@ "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "jest", "test:watch": "jest --watch", - "test:package": "node test/types/index.js" + "test:package": "node test/types/index.js", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/vue/package.json b/packages/vue/package.json index 4fe6bb3bd465..62d15e3c9175 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -49,7 +49,8 @@ "lint:eslint": "eslint . --format stylish", "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", "test": "jest", - "test:watch": "jest --watch" + "test:watch": "jest --watch", + "yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push" }, "volta": { "extends": "../../package.json" diff --git a/packages/wasm/package.json b/packages/wasm/package.json index a8fc6d165079..fcf87880529d 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -40,7 +40,8 @@ "fix:prettier": "prettier --write \"{src,test,scripts}/**/**.ts\"", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --format stylish", - "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"" + "lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"", + "yalc:publish": "ts-node ../../scripts/prepack.ts --bundles && yalc publish ./build/npm --push" }, "volta": { "extends": "../../package.json" diff --git a/yarn.lock b/yarn.lock index d39f972d5311..554a09d17be1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15070,7 +15070,7 @@ ini@1.3.6: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.6.tgz#f1c46a2a93a253e7b3905115e74d527cd23061a1" integrity sha512-IZUoxEjNjubzrmvzZU4lKP7OnYmX72XRl3sqkfJhBKweKi5rnGi5+IUdlj/H1M+Ip5JQ1WzaDMOBRY90Ajc5jg== -ini@2.0.0: +ini@2.0.0, ini@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== @@ -19456,6 +19456,16 @@ npm-packlist@^2.1.4: npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" +npm-packlist@^2.1.5: + version "2.2.2" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.2.2.tgz#076b97293fa620f632833186a7a8f65aaa6148c8" + integrity sha512-Jt01acDvJRhJGthnUJVF/w6gumWOZxO7IkpY/lsX9//zqQgnF7OJaxgQXcerd4uQOLu7W5bkb4mChL9mdfm+Zg== + dependencies: + glob "^7.1.6" + ignore-walk "^3.0.3" + npm-bundled "^1.1.1" + npm-normalize-package-bin "^1.0.1" + npm-packlist@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-3.0.0.tgz#0370df5cfc2fcc8f79b8f42b37798dd9ee32c2a9" @@ -27798,6 +27808,20 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yalc@^1.0.0-pre.53: + version "1.0.0-pre.53" + resolved "https://registry.yarnpkg.com/yalc/-/yalc-1.0.0-pre.53.tgz#c51db2bb924a6908f4cb7e82af78f7e5606810bc" + integrity sha512-tpNqBCpTXplnduzw5XC+FF8zNJ9L/UXmvQyyQj7NKrDNavbJtHvzmZplL5ES/RCnjX7JR7W9wz5GVDXVP3dHUQ== + dependencies: + chalk "^4.1.0" + detect-indent "^6.0.0" + fs-extra "^8.0.1" + glob "^7.1.4" + ignore "^5.0.4" + ini "^2.0.0" + npm-packlist "^2.1.5" + yargs "^16.1.1" + yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" From 86cd7fda49d9ec549ca1de0c47f67294e67af6a4 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Mon, 20 Mar 2023 09:22:21 +0100 Subject: [PATCH 42/54] fix(replay): Never capture file input changes (#7485) --- .../suites/replay/fileInput/init.js | 19 ++++++ .../suites/replay/fileInput/template.html | 9 +++ .../suites/replay/fileInput/test.ts | 58 +++++++++++++++++++ packages/replay/src/util/getPrivacyOptions.ts | 2 +- .../replay/test/integration/rrweb.test.ts | 2 +- .../test/unit/util/getPrivacyOptions.test.ts | 6 +- 6 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 packages/browser-integration-tests/suites/replay/fileInput/init.js create mode 100644 packages/browser-integration-tests/suites/replay/fileInput/template.html create mode 100644 packages/browser-integration-tests/suites/replay/fileInput/test.ts diff --git a/packages/browser-integration-tests/suites/replay/fileInput/init.js b/packages/browser-integration-tests/suites/replay/fileInput/init.js new file mode 100644 index 000000000000..a09c517b6a92 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/fileInput/init.js @@ -0,0 +1,19 @@ +import * as Sentry from '@sentry/browser'; +import { Replay } from '@sentry/replay'; + +window.Sentry = Sentry; +window.Replay = new Replay({ + flushMinDelay: 200, + flushMaxDelay: 200, + useCompression: false, + maskAllInputs: false, +}); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 0, + replaysSessionSampleRate: 1.0, + replaysOnErrorSampleRate: 0.0, + + integrations: [window.Replay], +}); diff --git a/packages/browser-integration-tests/suites/replay/fileInput/template.html b/packages/browser-integration-tests/suites/replay/fileInput/template.html new file mode 100644 index 000000000000..eb183083d872 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/fileInput/template.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/browser-integration-tests/suites/replay/fileInput/test.ts b/packages/browser-integration-tests/suites/replay/fileInput/test.ts new file mode 100644 index 000000000000..685c626ec470 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/fileInput/test.ts @@ -0,0 +1,58 @@ +import { expect } from '@playwright/test'; +import { IncrementalSource } from '@sentry-internal/rrweb'; +import type { inputData } from '@sentry-internal/rrweb/typings/types'; + +import { sentryTest } from '../../../utils/fixtures'; +import type { IncrementalRecordingSnapshot } from '../../../utils/replayHelpers'; +import { + getIncrementalRecordingSnapshots, + shouldSkipReplayTest, + waitForReplayRequest, +} from '../../../utils/replayHelpers'; + +function isInputMutation( + snap: IncrementalRecordingSnapshot, +): snap is IncrementalRecordingSnapshot & { data: inputData } { + return snap.data.source == IncrementalSource.Input; +} + +sentryTest( + 'should not capture file input mutations', + async ({ forceFlushReplay, getLocalTestPath, page, browserName }) => { + // This seems to be flaky on webkit, so skipping there + if (shouldSkipReplayTest() || browserName === 'webkit') { + sentryTest.skip(); + } + + const reqPromise0 = waitForReplayRequest(page, 0); + const reqPromise1 = waitForReplayRequest(page, 1); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.goto(url); + + await reqPromise0; + + await page.setInputFiles('#file-input', { + name: 'file.csv', + mimeType: 'text/csv', + buffer: Buffer.from('this,is,test'), + }); + + await forceFlushReplay(); + + const res1 = await reqPromise1; + + const snapshots = getIncrementalRecordingSnapshots(res1).filter(isInputMutation); + + expect(snapshots).toEqual([]); + }, +); diff --git a/packages/replay/src/util/getPrivacyOptions.ts b/packages/replay/src/util/getPrivacyOptions.ts index a8655bdf76af..a2aec1dcdb9d 100644 --- a/packages/replay/src/util/getPrivacyOptions.ts +++ b/packages/replay/src/util/getPrivacyOptions.ts @@ -88,7 +88,7 @@ export function getPrivacyOptions({ blockSelector, ), unblockSelector: getOption(unblock, ['.sentry-unblock', '[data-sentry-unblock]']), - ignoreSelector: getOption(ignore, ['.sentry-ignore', '[data-sentry-ignore]'], ignoreClass), + ignoreSelector: getOption(ignore, ['.sentry-ignore', '[data-sentry-ignore]', 'input[type="file"]'], ignoreClass), }; if (blockClass instanceof RegExp) { diff --git a/packages/replay/test/integration/rrweb.test.ts b/packages/replay/test/integration/rrweb.test.ts index 3e37df5f2910..cc40061b4a3d 100644 --- a/packages/replay/test/integration/rrweb.test.ts +++ b/packages/replay/test/integration/rrweb.test.ts @@ -20,7 +20,7 @@ describe('Integration | rrweb', () => { "blockSelector": ".sentry-block,[data-sentry-block],base[href=\\"/\\"],img,image,svg,video,object,picture,embed,map,audio,link[rel=\\"icon\\"],link[rel=\\"apple-touch-icon\\"]", "collectFonts": true, "emit": [Function], - "ignoreSelector": ".sentry-test-ignore,.sentry-ignore,[data-sentry-ignore]", + "ignoreSelector": ".sentry-test-ignore,.sentry-ignore,[data-sentry-ignore],input[type=\\"file\\"]", "inlineImages": false, "inlineStylesheet": true, "maskAllInputs": true, diff --git a/packages/replay/test/unit/util/getPrivacyOptions.test.ts b/packages/replay/test/unit/util/getPrivacyOptions.test.ts index 3f13f6410a35..27cf839fdd3b 100644 --- a/packages/replay/test/unit/util/getPrivacyOptions.test.ts +++ b/packages/replay/test/unit/util/getPrivacyOptions.test.ts @@ -20,7 +20,7 @@ describe('Unit | util | getPrivacyOptions', () => { ).toMatchInlineSnapshot(` Object { "blockSelector": ".custom-block,.sentry-block,[data-sentry-block],base[href=\\"/\\"]", - "ignoreSelector": ".custom-ignore,.sentry-ignore,[data-sentry-ignore]", + "ignoreSelector": ".custom-ignore,.sentry-ignore,[data-sentry-ignore],input[type=\\"file\\"]", "maskInputSelector": ".custom-mask,.sentry-mask,[data-sentry-mask]", "maskTextSelector": ".custom-mask,.sentry-mask,[data-sentry-mask]", "unblockSelector": ".custom-unblock,.sentry-unblock,[data-sentry-unblock]", @@ -48,7 +48,7 @@ describe('Unit | util | getPrivacyOptions', () => { ).toMatchInlineSnapshot(` Object { "blockSelector": ".custom-block,.deprecated-block-selector,.sentry-block,[data-sentry-block],base[href=\\"/\\"],.deprecated-block-class", - "ignoreSelector": ".custom-ignore,.sentry-ignore,[data-sentry-ignore],.deprecated-ignore-class", + "ignoreSelector": ".custom-ignore,.sentry-ignore,[data-sentry-ignore],input[type=\\"file\\"],.deprecated-ignore-class", "maskInputSelector": ".custom-mask,.deprecated-mask-selector,.sentry-mask,[data-sentry-mask],.deprecated-mask-class", "maskTextSelector": ".custom-mask,.deprecated-mask-selector,.sentry-mask,[data-sentry-mask],.deprecated-mask-class", "unblockSelector": ".custom-unblock,.sentry-unblock,[data-sentry-unblock]", @@ -74,7 +74,7 @@ describe('Unit | util | getPrivacyOptions', () => { Object { "blockClass": /deprecated-block-\\*/, "blockSelector": ".custom-block,.sentry-block,[data-sentry-block],base[href=\\"/\\"]", - "ignoreSelector": ".custom-ignore,.sentry-ignore,[data-sentry-ignore]", + "ignoreSelector": ".custom-ignore,.sentry-ignore,[data-sentry-ignore],input[type=\\"file\\"]", "maskInputSelector": ".custom-mask,.sentry-mask,[data-sentry-mask]", "maskTextClass": /deprecated-mask-\\*/, "maskTextSelector": ".custom-mask,.sentry-mask,[data-sentry-mask]", From 1cff6973815f170876d0cf76a08746b4bb48baaa Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Mon, 20 Mar 2023 04:32:04 -0400 Subject: [PATCH 43/54] feat(replay): Upgrade `rrweb` and `rrweb-player` (#7508) --------- Co-authored-by: Francesco Novy --- CHANGELOG.md | 9 +++++++++ .../suites/replay/customEvents/test.ts | 5 +---- packages/replay/package.json | 4 ++-- yarn.lock | 18 +++++++++--------- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49a101e7a76f..6829ddefa579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +**Replay `rrweb` changes:** + +`@sentry-internal/rrweb` was updated from 1.105.0 to 1.106.0: + +- feat: Ensure password inputs are always masked ([#78](https://github.com/getsentry/rrweb/pull/78)) +- fix: Ensure text masking for updated attributes works ([#83](https://github.com/getsentry/rrweb/pull/83)) +- fix: Ensure unmaskTextSelector is used for masked attributes ([#81](https://github.com/getsentry/rrweb/pull/81)) +- fix: Mask