diff --git a/.size-limit.js b/.size-limit.js index 38a83445d021..a42719ba096b 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -82,7 +82,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/prod/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'replayCanvasIntegration'), gzip: true, - limit: '86 KB', + limit: '87 KB', }, { name: '@sentry/browser (incl. Tracing, Replay, Feedback)', @@ -255,7 +255,7 @@ module.exports = [ path: createCDNPath('bundle.tracing.logs.metrics.min.js'), gzip: false, brotli: false, - limit: '131 KB', + limit: '132 KB', }, { name: 'CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed', @@ -269,7 +269,7 @@ module.exports = [ path: createCDNPath('bundle.tracing.replay.min.js'), gzip: false, brotli: false, - limit: '245 KB', + limit: '246 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed', @@ -308,7 +308,7 @@ module.exports = [ import: createImport('init'), ignore: ['$app/stores'], gzip: true, - limit: '43 KB', + limit: '44 KB', }, // Node-Core SDK (ESM) { diff --git a/packages/browser/src/tracing/browserTracingIntegration.ts b/packages/browser/src/tracing/browserTracingIntegration.ts index c71acf106258..2c5426aab783 100644 --- a/packages/browser/src/tracing/browserTracingIntegration.ts +++ b/packages/browser/src/tracing/browserTracingIntegration.ts @@ -54,6 +54,22 @@ import { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from export const BROWSER_TRACING_INTEGRATION_ID = 'BrowserTracing'; +/** + * We don't want to start a bunch of idle timers and PerformanceObservers + * for web crawlers, as they may prevent the page from being seen as "idle" + * by the crawler's rendering engine (e.g. Googlebot's headless Chromium). + */ +const BOT_USER_AGENT_RE = + /Googlebot|Google-InspectionTool|Storebot-Google|Bingbot|Slurp|DuckDuckBot|Baiduspider|YandexBot|Facebot|facebookexternalhit|LinkedInBot|Twitterbot|Applebot/i; + +function _isBotUserAgent(): boolean { + const nav = WINDOW.navigator as Navigator | undefined; + if (!nav?.userAgent) { + return false; + } + return BOT_USER_AGENT_RE.test(nav.userAgent); +} + interface RouteInfo { name: string | undefined; source: TransactionSource | undefined; @@ -384,6 +400,8 @@ export const browserTracingIntegration = ((options: Partial void); let lastInteractionTimestamp: number | undefined; @@ -484,6 +502,11 @@ export const browserTracingIntegration = ((options: Partial { Object.defineProperty(WINDOW, 'history', { value: originalGlobalHistory }); }); + describe('bot user agent detection', () => { + let originalNavigator: Navigator; + + beforeEach(() => { + originalNavigator = WINDOW.navigator; + }); + + afterEach(() => { + Object.defineProperty(WINDOW, 'navigator', { value: originalNavigator, writable: true, configurable: true }); + }); + + function setUserAgent(ua: string): void { + Object.defineProperty(WINDOW, 'navigator', { + value: { userAgent: ua }, + writable: true, + configurable: true, + }); + } + + it.each([ + 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)', + 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/W.X.Y.Z Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)', + 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Bingbot/2.0; +http://www.bing.com/bingbot.htm) Chrome/W.X.Y.Z Safari/537.36', + 'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)', + 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)', + 'LinkedInBot/1.0 (compatible; Mozilla/5.0)', + 'Twitterbot/1.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15 (Applebot/0.1)', + 'Mozilla/5.0 (compatible; Google-InspectionTool/1.0)', + ])('skips tracing setup for bot user agent: %s', ua => { + setUserAgent(ua); + + const client = new BrowserClient( + getDefaultBrowserClientOptions({ + tracesSampleRate: 1, + integrations: [browserTracingIntegration()], + }), + ); + setCurrentClient(client); + client.init(); + + expect(getActiveSpan()).toBeUndefined(); + }); + + it('does not skip tracing setup for normal user agents', () => { + setUserAgent( + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + ); + + const client = new BrowserClient( + getDefaultBrowserClientOptions({ + tracesSampleRate: 1, + integrations: [browserTracingIntegration()], + }), + ); + setCurrentClient(client); + client.init(); + + expect(getActiveSpan()).toBeDefined(); + }); + }); + it('works with tracing enabled', () => { const client = new BrowserClient( getDefaultBrowserClientOptions({ diff --git a/packages/nextjs/test/config/conflictingDebugOptions.test.ts b/packages/nextjs/test/config/conflictingDebugOptions.test.ts index 8c0920382c4a..5e7a46997b2b 100644 --- a/packages/nextjs/test/config/conflictingDebugOptions.test.ts +++ b/packages/nextjs/test/config/conflictingDebugOptions.test.ts @@ -20,7 +20,15 @@ describe('debug: true + removeDebugLogging warning', () => { let originalLocation: unknown; let originalAddEventListener: unknown; - beforeAll(() => { + beforeAll(async () => { + // Pre-warm V8 compilation cache for the large SDK module graphs. + // Without this, the first dynamic import after vi.resetModules() can hang + // because vitest needs to compile the entire module graph from scratch. + await import('../../src/client/index.js'); + await import('../../src/server/index.js'); + await import('../../src/edge/index.js'); + vi.resetModules(); + dom = new JSDOM('', { url: 'https://example.com/' }); originalDocument = (globalThis as any).document;