From ba9ff45d4910418eab416fea644a624b2aafaafc Mon Sep 17 00:00:00 2001 From: JPeer264 Date: Tue, 27 Jan 2026 13:05:20 +0100 Subject: [PATCH 1/2] feat(browser): Add mode option for the browser session integration --- .../suites/sessions/navigation-mode/init.js | 8 ++++ .../sessions/navigation-mode/subject.js | 6 +++ .../sessions/navigation-mode/template.html | 9 ++++ .../suites/sessions/navigation-mode/test.ts | 24 ++++++++++ .../suites/sessions/single-mode/init.js | 9 ++++ .../suites/sessions/single-mode/subject.js | 7 +++ .../suites/sessions/single-mode/template.html | 9 ++++ .../suites/sessions/single-mode/test.ts | 45 +++++++++++++++++++ packages/browser/src/index.ts | 1 + .../src/integrations/browsersession.ts | 37 +++++++++++---- 10 files changed, 146 insertions(+), 9 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/sessions/navigation-mode/init.js create mode 100644 dev-packages/browser-integration-tests/suites/sessions/navigation-mode/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/sessions/navigation-mode/template.html create mode 100644 dev-packages/browser-integration-tests/suites/sessions/navigation-mode/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/sessions/single-mode/init.js create mode 100644 dev-packages/browser-integration-tests/suites/sessions/single-mode/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/sessions/single-mode/template.html create mode 100644 dev-packages/browser-integration-tests/suites/sessions/single-mode/test.ts diff --git a/dev-packages/browser-integration-tests/suites/sessions/navigation-mode/init.js b/dev-packages/browser-integration-tests/suites/sessions/navigation-mode/init.js new file mode 100644 index 000000000000..af2df91a7ceb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/sessions/navigation-mode/init.js @@ -0,0 +1,8 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '0.1', +}); diff --git a/dev-packages/browser-integration-tests/suites/sessions/navigation-mode/subject.js b/dev-packages/browser-integration-tests/suites/sessions/navigation-mode/subject.js new file mode 100644 index 000000000000..41f372c3aa79 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/sessions/navigation-mode/subject.js @@ -0,0 +1,6 @@ +let clickCount = 0; + +document.getElementById('navigate').addEventListener('click', () => { + clickCount++; + history.pushState({}, '', `/page-${clickCount}`); +}); diff --git a/dev-packages/browser-integration-tests/suites/sessions/navigation-mode/template.html b/dev-packages/browser-integration-tests/suites/sessions/navigation-mode/template.html new file mode 100644 index 000000000000..2a1b5d400981 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/sessions/navigation-mode/template.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/sessions/navigation-mode/test.ts b/dev-packages/browser-integration-tests/suites/sessions/navigation-mode/test.ts new file mode 100644 index 000000000000..a9ab937a1816 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/sessions/navigation-mode/test.ts @@ -0,0 +1,24 @@ +import { expect } from '@playwright/test'; +import type { SessionContext } from '@sentry/core'; +import { sentryTest } from '../../../utils/fixtures'; +import { getMultipleSentryEnvelopeRequests } from '../../../utils/helpers'; + +sentryTest('should start new sessions on pushState navigation in default mode.', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + const sessionsPromise = getMultipleSentryEnvelopeRequests(page, 10, { + url, + envelopeType: 'session', + timeout: 4000, + }); + + await page.waitForSelector('#navigate'); + + await page.locator('#navigate').click(); + await page.locator('#navigate').click(); + await page.locator('#navigate').click(); + + const sessions = (await sessionsPromise).filter(session => session.init); + + expect(sessions.length).toBe(3); +}); diff --git a/dev-packages/browser-integration-tests/suites/sessions/single-mode/init.js b/dev-packages/browser-integration-tests/suites/sessions/single-mode/init.js new file mode 100644 index 000000000000..890a54f166eb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/sessions/single-mode/init.js @@ -0,0 +1,9 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '0.1', + integrations: [Sentry.browserSessionIntegration({ mode: 'single' })], +}); diff --git a/dev-packages/browser-integration-tests/suites/sessions/single-mode/subject.js b/dev-packages/browser-integration-tests/suites/sessions/single-mode/subject.js new file mode 100644 index 000000000000..97a4037f17bb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/sessions/single-mode/subject.js @@ -0,0 +1,7 @@ +let clickCount = 0; + +document.getElementById('navigate').addEventListener('click', () => { + clickCount++; + // Each click navigates to a different page + history.pushState({}, '', `/page-${clickCount}`); +}); diff --git a/dev-packages/browser-integration-tests/suites/sessions/single-mode/template.html b/dev-packages/browser-integration-tests/suites/sessions/single-mode/template.html new file mode 100644 index 000000000000..2a1b5d400981 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/sessions/single-mode/template.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/sessions/single-mode/test.ts b/dev-packages/browser-integration-tests/suites/sessions/single-mode/test.ts new file mode 100644 index 000000000000..c02c61eff450 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/sessions/single-mode/test.ts @@ -0,0 +1,45 @@ +import { expect } from '@playwright/test'; +import type { SessionContext } from '@sentry/core'; +import { sentryTest } from '../../../utils/fixtures'; +import { getMultipleSentryEnvelopeRequests } from '../../../utils/helpers'; + +sentryTest('should start a session on pageload in single mode.', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + const sessions = await getMultipleSentryEnvelopeRequests(page, 1, { + url, + envelopeType: 'session', + timeout: 2000, + }); + + expect(sessions.length).toBeGreaterThanOrEqual(1); + const session = sessions[0]; + expect(session).toBeDefined(); + expect(session.init).toBe(true); + expect(session.errors).toBe(0); + expect(session.status).toBe('ok'); +}); + +sentryTest( + 'should NOT start a new session on pushState navigation in single mode.', + async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + const sessionsPromise = getMultipleSentryEnvelopeRequests(page, 10, { + url, + envelopeType: 'session', + timeout: 4000, + }); + + await page.waitForSelector('#navigate'); + + await page.locator('#navigate').click(); + await page.locator('#navigate').click(); + await page.locator('#navigate').click(); + + const sessions = (await sessionsPromise).filter(session => session.init); + + expect(sessions.length).toBe(1); + expect(sessions[0].init).toBe(true); + }, +); diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 6e7c54198edc..d24ec3cd63de 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -76,6 +76,7 @@ export { makeBrowserOfflineTransport } from './transports/offline'; export { browserProfilingIntegration } from './profiling/integration'; export { spotlightBrowserIntegration } from './integrations/spotlight'; export { browserSessionIntegration } from './integrations/browsersession'; +export type { BrowserSessionOptions } from './integrations/browsersession'; export { launchDarklyIntegration, buildLaunchDarklyFlagUsedHandler } from './integrations/featureFlags/launchdarkly'; export { openFeatureIntegration, OpenFeatureIntegrationHook } from './integrations/featureFlags/openfeature'; export { unleashIntegration } from './integrations/featureFlags/unleash'; diff --git a/packages/browser/src/integrations/browsersession.ts b/packages/browser/src/integrations/browsersession.ts index 78a9228e3b29..10b6e0fcc05e 100644 --- a/packages/browser/src/integrations/browsersession.ts +++ b/packages/browser/src/integrations/browsersession.ts @@ -3,13 +3,30 @@ import { addHistoryInstrumentationHandler } from '@sentry-internal/browser-utils import { DEBUG_BUILD } from '../debug-build'; import { WINDOW } from '../helpers'; +export interface BrowserSessionOptions { + /** + * Controls when sessions are created. + * + * - `'single'`: A session is created once when the page is loaded. Session is not + * updated on navigation. This is useful for webviews or single-page apps where + * URL changes should not trigger new sessions. + * - `'navigation'`: A session is created on page load and on every navigation. + * This is the default behavior. + * + * @default 'navigation' + */ + mode?: 'single' | 'navigation'; +} + /** * When added, automatically creates sessions which allow you to track adoption and crashes (crash free rate) in your Releases in Sentry. * More information: https://docs.sentry.io/product/releases/health/ * * Note: In order for session tracking to work, you need to set up Releases: https://docs.sentry.io/product/releases/ */ -export const browserSessionIntegration = defineIntegration(() => { +export const browserSessionIntegration = defineIntegration((options: BrowserSessionOptions = {}) => { + const mode = options.mode ?? 'navigation'; + return { name: 'BrowserSession', setupOnce() { @@ -26,14 +43,16 @@ export const browserSessionIntegration = defineIntegration(() => { startSession({ ignoreDuration: true }); captureSession(); - // We want to create a session for every navigation as well - addHistoryInstrumentationHandler(({ from, to }) => { - // Don't create an additional session for the initial route or if the location did not change - if (from !== undefined && from !== to) { - startSession({ ignoreDuration: true }); - captureSession(); - } - }); + if (mode === 'navigation') { + // We want to create a session for every navigation as well + addHistoryInstrumentationHandler(({ from, to }) => { + // Don't create an additional session for the initial route or if the location did not change + if (from !== undefined && from !== to) { + startSession({ ignoreDuration: true }); + captureSession(); + } + }); + } }, }; }); From 452d0c4f14062d1da2111ee18e0949cfdfad73ed Mon Sep 17 00:00:00 2001 From: JPeer264 Date: Tue, 27 Jan 2026 14:40:20 +0100 Subject: [PATCH 2/2] fixup! feat(browser): Add mode option for the browser session integration --- .size-limit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.size-limit.js b/.size-limit.js index 978d3105dd41..c9961f9b5ee7 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -230,7 +230,7 @@ module.exports = [ path: createCDNPath('bundle.tracing.logs.metrics.min.js'), gzip: false, brotli: false, - limit: '130 KB', + limit: '131 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay) - uncompressed',