diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8ab03a313253..b18da4f6c43f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -818,10 +818,10 @@ jobs:
pattern: profiling-node-binaries-${{ github.sha }}-*
path: ${{ github.workspace }}/packages/profiling-node/lib/
merge-multiple: true
+ # End rebuild profiling
- - name: Build Profiling tarball
+ - name: Build tarballs
run: yarn build:tarball
- # End rebuild profiling
- name: Stores tarballs in cache
uses: actions/cache/save@v4
@@ -867,6 +867,7 @@ jobs:
'create-remix-app-express',
'create-remix-app-express-legacy',
'create-remix-app-express-vite-dev',
+ 'default-browser',
'node-express-esm-loader',
'node-express-esm-preload',
'node-express-esm-without-loader',
diff --git a/.size-limit.js b/.size-limit.js
index 3de81b23fc5b..80aa4c5095ea 100644
--- a/.size-limit.js
+++ b/.size-limit.js
@@ -193,7 +193,7 @@ module.exports = [
import: createImport('init'),
ignore: ['next/router', 'next/constants'],
gzip: true,
- limit: '39 KB',
+ limit: '38.05 KB',
},
// SvelteKit SDK (ESM)
{
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 1a8f9ce92cfc..615ca5b24472 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -36,10 +36,11 @@
],
"deno.enablePaths": ["packages/deno/test"],
"editor.codeActionsOnSave": {
- "source.organizeImports.biome": "explicit",
+ "source.organizeImports.biome": "explicit"
},
"editor.defaultFormatter": "biomejs.biome",
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
- }
+ },
+ "cSpell.words": ["arrayify"]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 152f75a9169c..3f8110348cd6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,7 +10,7 @@
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
-Work in this release was contributed by @MonstraG. Thank you for your contribution!
+Work in this release was contributed by @MonstraG and @Zen-cronic. Thank you for your contributions!
## 8.25.0
diff --git a/dev-packages/e2e-tests/test-applications/default-browser/.gitignore b/dev-packages/e2e-tests/test-applications/default-browser/.gitignore
new file mode 100644
index 000000000000..84634c973eeb
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/default-browser/.gitignore
@@ -0,0 +1,29 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+/test-results/
+/playwright-report/
+/playwright/.cache/
+
+!*.d.ts
diff --git a/dev-packages/e2e-tests/test-applications/default-browser/.npmrc b/dev-packages/e2e-tests/test-applications/default-browser/.npmrc
new file mode 100644
index 000000000000..070f80f05092
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/default-browser/.npmrc
@@ -0,0 +1,2 @@
+@sentry:registry=http://127.0.0.1:4873
+@sentry-internal:registry=http://127.0.0.1:4873
diff --git a/dev-packages/e2e-tests/test-applications/default-browser/build.mjs b/dev-packages/e2e-tests/test-applications/default-browser/build.mjs
new file mode 100644
index 000000000000..aeaad894bdbd
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/default-browser/build.mjs
@@ -0,0 +1,49 @@
+import * as path from 'path';
+import * as url from 'url';
+import HtmlWebpackPlugin from 'html-webpack-plugin';
+import TerserPlugin from 'terser-webpack-plugin';
+import webpack from 'webpack';
+
+const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
+
+webpack(
+ {
+ entry: path.join(__dirname, 'src/index.js'),
+ output: {
+ path: path.join(__dirname, 'build'),
+ filename: 'app.js',
+ },
+ optimization: {
+ minimize: true,
+ minimizer: [new TerserPlugin()],
+ },
+ plugins: [
+ new webpack.EnvironmentPlugin(['E2E_TEST_DSN']),
+ new HtmlWebpackPlugin({
+ template: path.join(__dirname, 'public/index.html'),
+ }),
+ ],
+ mode: 'production',
+ },
+ (err, stats) => {
+ if (err) {
+ console.error(err.stack || err);
+ if (err.details) {
+ console.error(err.details);
+ }
+ return;
+ }
+
+ const info = stats.toJson();
+
+ if (stats.hasErrors()) {
+ console.error(info.errors);
+ process.exit(1);
+ }
+
+ if (stats.hasWarnings()) {
+ console.warn(info.warnings);
+ process.exit(1);
+ }
+ },
+);
diff --git a/dev-packages/e2e-tests/test-applications/default-browser/package.json b/dev-packages/e2e-tests/test-applications/default-browser/package.json
new file mode 100644
index 000000000000..d6286c2423b6
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/default-browser/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "default-browser-test-app",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@sentry/browser": "latest || *",
+ "@types/node": "16.7.13",
+ "typescript": "4.9.5"
+ },
+ "scripts": {
+ "start": "serve -s build",
+ "build": "node build.mjs",
+ "test": "playwright test",
+ "clean": "npx rimraf node_modules pnpm-lock.yaml",
+ "test:build": "pnpm install && npx playwright install && pnpm build",
+ "test:assert": "pnpm test"
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ },
+ "devDependencies": {
+ "@playwright/test": "^1.44.1",
+ "@sentry-internal/test-utils": "link:../../../test-utils",
+ "webpack": "^5.91.0",
+ "serve": "14.0.1",
+ "terser-webpack-plugin": "^5.3.10",
+ "html-webpack-plugin": "^5.6.0"
+ },
+ "volta": {
+ "extends": "../../package.json"
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/default-browser/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/default-browser/playwright.config.mjs
new file mode 100644
index 000000000000..31f2b913b58b
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/default-browser/playwright.config.mjs
@@ -0,0 +1,7 @@
+import { getPlaywrightConfig } from '@sentry-internal/test-utils';
+
+const config = getPlaywrightConfig({
+ startCommand: `pnpm start`,
+});
+
+export default config;
diff --git a/dev-packages/e2e-tests/test-applications/default-browser/public/index.html b/dev-packages/e2e-tests/test-applications/default-browser/public/index.html
new file mode 100644
index 000000000000..35e91be91c84
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/default-browser/public/index.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+ Default Browser App
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/default-browser/src/index.js b/dev-packages/e2e-tests/test-applications/default-browser/src/index.js
new file mode 100644
index 000000000000..d3eea216fe84
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/default-browser/src/index.js
@@ -0,0 +1,18 @@
+import * as Sentry from '@sentry/browser';
+
+Sentry.init({
+ dsn: process.env.E2E_TEST_DSN,
+ integrations: [Sentry.browserTracingIntegration()],
+ tracesSampleRate: 1.0,
+ release: 'e2e-test',
+ environment: 'qa',
+ tunnel: 'http://localhost:3031',
+});
+
+document.getElementById('exception-button').addEventListener('click', () => {
+ throw new Error('I am an error!');
+});
+
+document.getElementById('navigation-link').addEventListener('click', () => {
+ document.getElementById('navigation-target').scrollIntoView({ behavior: 'smooth' });
+});
diff --git a/dev-packages/e2e-tests/test-applications/default-browser/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/default-browser/start-event-proxy.mjs
new file mode 100644
index 000000000000..6c84e74d541b
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/default-browser/start-event-proxy.mjs
@@ -0,0 +1,6 @@
+import { startEventProxyServer } from '@sentry-internal/test-utils';
+
+startEventProxyServer({
+ port: 3031,
+ proxyServerName: 'default-browser',
+});
diff --git a/dev-packages/e2e-tests/test-applications/default-browser/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/default-browser/tests/errors.test.ts
new file mode 100644
index 000000000000..e4f2eda9a579
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/default-browser/tests/errors.test.ts
@@ -0,0 +1,58 @@
+import { expect, test } from '@playwright/test';
+import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
+
+test('captures an error', async ({ page }) => {
+ const errorEventPromise = waitForError('default-browser', event => {
+ return !event.type && event.exception?.values?.[0]?.value === 'I am an error!';
+ });
+
+ await page.goto('/');
+
+ const exceptionButton = page.locator('id=exception-button');
+ await exceptionButton.click();
+
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent.exception?.values).toHaveLength(1);
+ expect(errorEvent.exception?.values?.[0]?.value).toBe('I am an error!');
+
+ expect(errorEvent.transaction).toBe('/');
+
+ expect(errorEvent.request).toEqual({
+ url: 'http://localhost:3030/',
+ headers: expect.any(Object),
+ });
+
+ expect(errorEvent.contexts?.trace).toEqual({
+ trace_id: expect.any(String),
+ span_id: expect.any(String),
+ });
+});
+
+test('sets correct transactionName', async ({ page }) => {
+ const transactionPromise = waitForTransaction('default-browser', async transactionEvent => {
+ return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
+ });
+
+ const errorEventPromise = waitForError('default-browser', event => {
+ return !event.type && event.exception?.values?.[0]?.value === 'I am an error!';
+ });
+
+ await page.goto('/');
+ const transactionEvent = await transactionPromise;
+
+ const exceptionButton = page.locator('id=exception-button');
+ await exceptionButton.click();
+
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent.exception?.values).toHaveLength(1);
+ expect(errorEvent.exception?.values?.[0]?.value).toBe('I am an error!');
+
+ expect(errorEvent.transaction).toEqual('/');
+
+ expect(errorEvent.contexts?.trace).toEqual({
+ trace_id: transactionEvent.contexts?.trace?.trace_id,
+ span_id: expect.not.stringContaining(transactionEvent.contexts?.trace?.span_id || ''),
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/default-browser/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/default-browser/tests/performance.test.ts
new file mode 100644
index 000000000000..7013fb43ecef
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/default-browser/tests/performance.test.ts
@@ -0,0 +1,118 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/test-utils';
+
+test('captures a pageload transaction', async ({ page }) => {
+ const transactionPromise = waitForTransaction('default-browser', async transactionEvent => {
+ return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
+ });
+
+ await page.goto(`/`);
+
+ const pageLoadTransaction = await transactionPromise;
+
+ expect(pageLoadTransaction).toEqual({
+ contexts: {
+ trace: {
+ data: expect.objectContaining({
+ 'sentry.idle_span_finish_reason': 'idleTimeout',
+ 'sentry.op': 'pageload',
+ 'sentry.origin': 'auto.pageload.browser',
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'url',
+ }),
+ op: 'pageload',
+ origin: 'auto.pageload.browser',
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ },
+ },
+ environment: 'qa',
+ event_id: expect.stringMatching(/[a-f0-9]{32}/),
+ measurements: {
+ 'connection.rtt': {
+ unit: 'millisecond',
+ value: expect.any(Number),
+ },
+ fcp: {
+ unit: 'millisecond',
+ value: expect.any(Number),
+ },
+ fp: {
+ unit: 'millisecond',
+ value: expect.any(Number),
+ },
+ lcp: {
+ unit: 'millisecond',
+ value: expect.any(Number),
+ },
+ ttfb: {
+ unit: 'millisecond',
+ value: expect.any(Number),
+ },
+ 'ttfb.requestTime': {
+ unit: 'millisecond',
+ value: expect.any(Number),
+ },
+ },
+ platform: 'javascript',
+ release: 'e2e-test',
+ request: {
+ headers: {
+ 'User-Agent': expect.any(String),
+ },
+ url: 'http://localhost:3030/',
+ },
+ sdk: {
+ integrations: expect.any(Array),
+ name: 'sentry.javascript.browser',
+ packages: [
+ {
+ name: 'npm:@sentry/browser',
+ version: expect.any(String),
+ },
+ ],
+ version: expect.any(String),
+ },
+ spans: expect.any(Array),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ transaction: '/',
+ transaction_info: {
+ source: 'url',
+ },
+ type: 'transaction',
+ });
+});
+
+test('captures a navigation transaction', async ({ page }) => {
+ page.on('console', msg => console.log(msg.text()));
+ const pageLoadTransactionPromise = waitForTransaction('default-browser', async transactionEvent => {
+ return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
+ });
+
+ const navigationTransactionPromise = waitForTransaction('default-browser', async transactionEvent => {
+ return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation';
+ });
+
+ await page.goto(`/`);
+ await pageLoadTransactionPromise;
+
+ const linkElement = page.locator('id=navigation-link');
+
+ await linkElement.click();
+
+ const navigationTransaction = await navigationTransactionPromise;
+
+ expect(navigationTransaction).toMatchObject({
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.browser',
+ },
+ },
+ transaction: '/',
+ transaction_info: {
+ source: 'url',
+ },
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/default-browser/tsconfig.json b/dev-packages/e2e-tests/test-applications/default-browser/tsconfig.json
new file mode 100644
index 000000000000..4cc95dc2689a
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/default-browser/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "es2018",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noFallthroughCasesInSwitch": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react"
+ },
+ "include": ["src", "tests"]
+}
diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/app.vue b/dev-packages/e2e-tests/test-applications/nuxt-3/app.vue
index 06f3020220dd..4e7954ceb4af 100644
--- a/dev-packages/e2e-tests/test-applications/nuxt-3/app.vue
+++ b/dev-packages/e2e-tests/test-applications/nuxt-3/app.vue
@@ -3,6 +3,8 @@