diff --git a/.size-limit.js b/.size-limit.js index b841667d9d89..b7ff634f5278 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -64,6 +64,13 @@ module.exports = [ gzip: true, disablePlugins: ['@size-limit/esbuild'], }, + { + name: '@sentry/browser (incl. Tracing Core)', + path: 'packages/browser/build/npm/esm/prod/index.js', + import: createImport('init', 'browserTracingCoreIntegration'), + gzip: true, + disablePlugins: ['@size-limit/esbuild'], + }, { name: '@sentry/browser (incl. Tracing + Span Streaming)', path: 'packages/browser/build/npm/esm/prod/index.js', diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index dc8bde1ec231..ffda26d539ff 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -28,6 +28,7 @@ export { getFeedback, sendFeedback } from '@sentry/feedback'; export { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from './tracing/request'; export { + browserTracingCoreIntegration, browserTracingIntegration, startBrowserTracingNavigationSpan, startBrowserTracingPageLoadSpan, diff --git a/packages/browser/src/tracing/browserTracingIntegration.ts b/packages/browser/src/tracing/browserTracingIntegration.ts index 3959d0ac4d0b..25ab7da3d44d 100644 --- a/packages/browser/src/tracing/browserTracingIntegration.ts +++ b/packages/browser/src/tracing/browserTracingIntegration.ts @@ -1,5 +1,13 @@ /* eslint-disable max-lines */ -import type { Client, IntegrationFn, RequestHookInfo, ResponseHookInfo, Span, StartSpanOptions } from '@sentry/core'; +import type { + Client, + Integration, + IntegrationFn, + RequestHookInfo, + ResponseHookInfo, + Span, + StartSpanOptions, +} from '@sentry/core'; import { addNonEnumerableProperty, consoleSandbox, @@ -39,7 +47,7 @@ import { DEBUG_BUILD } from '../debug-build'; import { filterCollectedUrl } from '@sentry/core'; import { getHttpRequestData, WINDOW } from '../helpers'; import type { WebVitalsOptions } from '../integrations/webVitals'; -import { WEB_VITALS_INTEGRATION_NAME, webVitalsIntegration } from '../integrations/webVitals'; +import { webVitalsIntegration } from '../integrations/webVitals'; import { registerBackgroundTabDetection } from './backgroundtab'; import { linkTraces } from './linkedTraces'; import { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from './request'; @@ -274,7 +282,13 @@ export interface BrowserTracingOptions { onRequestSpanEnd?(span: Span, responseInformation: ResponseHookInfo): void; } -const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingOptions = { +/** + * Options for `browserTracingCoreIntegration`: the options of `browserTracingIntegration`, minus the ones + * for features that `browserTracingIntegration` registers as standalone integrations. + */ +export type BrowserTracingCoreOptions = Omit; + +const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingCoreOptions = { ...TRACING_DEFAULTS, instrumentNavigation: true, instrumentBfcacheRestore: true, @@ -282,8 +296,6 @@ const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingOptions = { markBackgroundSpan: true, enableLongTask: true, enableLongAnimationFrame: true, - // oxlint-disable-next-line typescript/no-deprecated -- still honoured until it is removed - enableInp: true, ignoreResourceSpans: [], detectRedirects: true, linkPreviousTrace: 'in-memory', @@ -311,6 +323,39 @@ export const browserTracingIntegration = ((options: Partial { + // Custom clients and test doubles may not implement it. + if (!client.addIntegration) { + return; + } + + const ignore = webVitals?.ignore ?? []; + client.addIntegration( + webVitalsIntegration({ + ...webVitals, + ignore: enableInp || ignore.includes('inp') ? ignore : [...ignore, 'inp'], + }), + ); + }); +}) satisfies IntegrationFn; + +/** + * The pageload and navigation spans of `browserTracingIntegration`, without the features it registers as + * standalone integrations (such as `webVitalsIntegration`). + * + * Use it instead of `browserTracingIntegration` to only bundle the integrations you add yourself. + */ +export const browserTracingCoreIntegration = ((options: Partial = {}) => + _browserTracingIntegration(options)) satisfies IntegrationFn; + +function _browserTracingIntegration( + options: Partial, + // Runs before the pageload span starts, so the integrations it adds can hook into it. + addDefaultIntegrations?: (client: Client) => void, +) { /** * This is just a small wrapper that makes `document` optional. * We want to be extra-safe and always check that this exists, to ensure weird environments do not blow up. @@ -318,11 +363,8 @@ export const browserTracingIntegration = ((options: Partial { expect(client.getIntegrationByName('WebVitals')).toBeDefined(); }); - it('does not auto-register when the user supplies their own webVitalsIntegration', () => { - const webVitalsSpy = vi.spyOn(webVitalsModule, 'webVitalsIntegration'); + it('does not register webVitalsIntegration in browserTracingCoreIntegration', () => { + const client = new BrowserClient( + getDefaultBrowserClientOptions({ + tracesSampleRate: 1, + integrations: [browserTracingCoreIntegration()], + }), + ); + setCurrentClient(client); + client.init(); + + expect(client.getIntegrationByName('BrowserTracing')).toBeDefined(); + expect(client.getIntegrationByName('WebVitals')).toBeUndefined(); + }); + + it('keeps the webVitalsIntegration the user supplies', () => { const userWebVitals = webVitalsModule.webVitalsIntegration({ softNavigations: false }); - webVitalsSpy.mockClear(); const client = new BrowserClient( getDefaultBrowserClientOptions({ @@ -224,7 +237,7 @@ describe('browserTracingIntegration', () => { setCurrentClient(client); client.init(); - expect(webVitalsSpy).not.toHaveBeenCalled(); + expect(client.getIntegrationByName('WebVitals')).toBe(userWebVitals); }); it('forwards webVitals options to the auto-registered integration', () => {