diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d8ccc2c75dec..516b1d47b624 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -847,6 +847,7 @@ jobs:
[
'angular-17',
'angular-18',
+ 'astro-4',
'aws-lambda-layer-cjs',
'aws-serverless-esm',
'node-express',
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/.gitignore b/dev-packages/e2e-tests/test-applications/astro-4/.gitignore
new file mode 100644
index 000000000000..560782d47d98
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/.gitignore
@@ -0,0 +1,26 @@
+# build output
+dist/
+
+# generated types
+.astro/
+
+# dependencies
+node_modules/
+
+# logs
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# environment variables
+.env
+.env.production
+
+# macOS-specific files
+.DS_Store
+
+# jetbrains setting folder
+.idea/
+
+test-results
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/.npmrc b/dev-packages/e2e-tests/test-applications/astro-4/.npmrc
new file mode 100644
index 000000000000..070f80f05092
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/.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/astro-4/README.md b/dev-packages/e2e-tests/test-applications/astro-4/README.md
new file mode 100644
index 000000000000..28e41344b910
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/README.md
@@ -0,0 +1,6 @@
+# Astro 4 E2E test app
+
+- Astro 4.x
+- Output mode `hybrid` (== opt into SSR routes)
+- Node adapter
+- Configured for Tracing and Performance
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/astro.config.mjs b/dev-packages/e2e-tests/test-applications/astro-4/astro.config.mjs
new file mode 100644
index 000000000000..96db58759ba2
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/astro.config.mjs
@@ -0,0 +1,29 @@
+import node from '@astrojs/node';
+import sentry from '@sentry/astro';
+import { defineConfig } from 'astro/config';
+
+import spotlightjs from '@spotlightjs/astro';
+
+// https://astro.build/config
+export default defineConfig({
+ output: 'hybrid',
+ integrations: [
+ sentry({
+ debug: true,
+ sourceMapsUploadOptions: {
+ enabled: false,
+ },
+ }),
+ spotlightjs(),
+ ],
+ adapter: node({
+ mode: 'standalone',
+ }),
+ vite: {
+ build: {
+ rollupOptions: {
+ external: ['https'],
+ },
+ },
+ },
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/package.json b/dev-packages/e2e-tests/test-applications/astro-4/package.json
new file mode 100644
index 000000000000..5bc5233ef7cc
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "astro-hybrid-with-static-routes",
+ "type": "module",
+ "version": "0.0.1",
+ "scripts": {
+ "dev": "astro dev --force",
+ "start": "astro dev",
+ "build": "astro check && astro build",
+ "preview": "astro preview",
+ "astro": "astro",
+ "test:build": "pnpm install && npx playwright install && pnpm build",
+ "test:assert": "TEST_ENV=production playwright test"
+ },
+ "dependencies": {
+ "@astrojs/check": "^0.9.2",
+ "@astrojs/node": "^8.3.2",
+ "@playwright/test": "^1.46.0",
+ "@sentry/astro": "* || latest",
+ "@sentry-internal/test-utils": "link:../../../test-utils",
+ "@spotlightjs/astro": "^2.1.6",
+ "astro": "^4.13.3",
+ "typescript": "^5.5.4"
+ },
+ "devDependencies": {
+ "@astrojs/internal-helpers": "^0.4.1"
+ }
+}
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/astro-4/playwright.config.mjs
new file mode 100644
index 000000000000..cd6ed611fb4a
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/playwright.config.mjs
@@ -0,0 +1,13 @@
+import { getPlaywrightConfig } from '@sentry-internal/test-utils';
+
+const testEnv = process.env.TEST_ENV;
+
+if (!testEnv) {
+ throw new Error('No test env defined');
+}
+
+const config = getPlaywrightConfig({
+ startCommand: 'node ./dist/server/entry.mjs',
+});
+
+export default config;
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/public/favicon.svg b/dev-packages/e2e-tests/test-applications/astro-4/public/favicon.svg
new file mode 100644
index 000000000000..f157bd1c5e28
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/public/favicon.svg
@@ -0,0 +1,9 @@
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/sentry.client.config.js b/dev-packages/e2e-tests/test-applications/astro-4/sentry.client.config.js
new file mode 100644
index 000000000000..2b79ec0ed337
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/sentry.client.config.js
@@ -0,0 +1,8 @@
+import * as Sentry from '@sentry/astro';
+
+Sentry.init({
+ dsn: import.meta.env.PUBLIC_E2E_TEST_DSN,
+ environment: 'qa',
+ tracesSampleRate: 1.0,
+ tunnel: 'http://localhost:3031/', // proxy server
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/sentry.server.config.js b/dev-packages/e2e-tests/test-applications/astro-4/sentry.server.config.js
new file mode 100644
index 000000000000..0662d678dc7c
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/sentry.server.config.js
@@ -0,0 +1,9 @@
+import * as Sentry from '@sentry/astro';
+
+Sentry.init({
+ dsn: import.meta.env.PUBLIC_E2E_TEST_DSN,
+ environment: 'qa',
+ tracesSampleRate: 1.0,
+ spotlight: true,
+ tunnel: 'http://localhost:3031/', // proxy server
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/env.d.ts b/dev-packages/e2e-tests/test-applications/astro-4/src/env.d.ts
new file mode 100644
index 000000000000..f964fe0cffd8
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/layouts/Layout.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/layouts/Layout.astro
new file mode 100644
index 000000000000..c4e54b834656
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/layouts/Layout.astro
@@ -0,0 +1,39 @@
+---
+interface Props {
+ title: string;
+}
+
+const { title } = Astro.props;
+---
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/client-error/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/client-error/index.astro
new file mode 100644
index 000000000000..facd6f077a6e
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/client-error/index.astro
@@ -0,0 +1,11 @@
+---
+import Layout from "../../layouts/Layout.astro";
+---
+
+
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/api.ts b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/api.ts
new file mode 100644
index 000000000000..a76accdba010
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/api.ts
@@ -0,0 +1,15 @@
+import type { APIRoute } from 'astro';
+
+export const prerender = false;
+
+export const GET: APIRoute = ({ request, url }) => {
+ if (url.searchParams.has('error')) {
+ throw new Error('Endpoint Error');
+ }
+ return new Response(
+ JSON.stringify({
+ search: url.search,
+ sp: url.searchParams,
+ }),
+ );
+};
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/index.astro
new file mode 100644
index 000000000000..f025c76f8365
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/index.astro
@@ -0,0 +1,9 @@
+---
+import Layout from "../../layouts/Layout.astro";
+
+export const prerender = false;
+---
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/index.astro
new file mode 100644
index 000000000000..088205fc4028
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/index.astro
@@ -0,0 +1,36 @@
+---
+import Layout from '../layouts/Layout.astro';
+---
+
+
+
+ Astro E2E Test App
+
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/ssr-error/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/ssr-error/index.astro
new file mode 100644
index 000000000000..4ecb7466de70
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/ssr-error/index.astro
@@ -0,0 +1,13 @@
+---
+import Layout from "../../layouts/Layout.astro";
+
+const a = {} as any;
+console.log(a.foo.x);
+export const prerender = false;
+---
+
+
+
+ Page with SSR error
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-ssr/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-ssr/index.astro
new file mode 100644
index 000000000000..58f5d80198d7
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-ssr/index.astro
@@ -0,0 +1,15 @@
+---
+import Layout from "../../layouts/Layout.astro"
+
+export const prerender = false
+---
+
+
+
+
+ This is a server page
+
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-static/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-static/index.astro
new file mode 100644
index 000000000000..f71bf00c9adf
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-static/index.astro
@@ -0,0 +1,15 @@
+---
+import Layout from "../../layouts/Layout.astro";
+
+export const prerender = true;
+---
+
+
+
+
+ This is a static page
+
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/astro-4/start-event-proxy.mjs
new file mode 100644
index 000000000000..a657dae0f425
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/start-event-proxy.mjs
@@ -0,0 +1,6 @@
+import { startEventProxyServer } from '@sentry-internal/test-utils';
+
+startEventProxyServer({
+ port: 3031,
+ proxyServerName: 'astro-4',
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.client.test.ts
new file mode 100644
index 000000000000..4cbf4bf36604
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.client.test.ts
@@ -0,0 +1,79 @@
+import { expect, test } from '@playwright/test';
+import { waitForError } from '@sentry-internal/test-utils';
+
+test.describe('client-side errors', () => {
+ test('captures error thrown on click', async ({ page }) => {
+ const errorEventPromise = waitForError('astro-4', errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === 'client error';
+ });
+
+ await page.goto('/client-error');
+
+ await page.getByText('Throw Error').click();
+
+ const errorEvent = await errorEventPromise;
+
+ const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames;
+
+ expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual(
+ expect.objectContaining({
+ colno: expect.any(Number),
+ lineno: expect.any(Number),
+ filename: expect.stringContaining('/client-error'),
+ function: 'HTMLButtonElement.onclick',
+ in_app: true,
+ }),
+ );
+
+ expect(errorEvent).toMatchObject({
+ exception: {
+ values: [
+ {
+ mechanism: {
+ handled: false,
+ type: 'onerror',
+ },
+ type: 'Error',
+ value: 'client error',
+ stacktrace: expect.any(Object), // detailed check above
+ },
+ ],
+ },
+ level: 'error',
+ platform: 'javascript',
+ request: {
+ url: expect.stringContaining('/client-error'),
+ headers: {
+ 'User-Agent': expect.any(String),
+ },
+ },
+ event_id: expect.stringMatching(/[a-f0-9]{32}/),
+ timestamp: expect.any(Number),
+ sdk: {
+ integrations: expect.arrayContaining([
+ 'InboundFilters',
+ 'FunctionToString',
+ 'BrowserApiErrors',
+ 'Breadcrumbs',
+ 'GlobalHandlers',
+ 'LinkedErrors',
+ 'Dedupe',
+ 'HttpContext',
+ 'BrowserTracing',
+ ]),
+ name: 'sentry.javascript.astro',
+ version: expect.any(String),
+ packages: expect.any(Array),
+ },
+ transaction: '/client-error',
+ contexts: {
+ trace: {
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ },
+ },
+ environment: 'qa',
+ });
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts
new file mode 100644
index 000000000000..d5f07ebe239a
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts
@@ -0,0 +1,115 @@
+import { expect, test } from '@playwright/test';
+import { waitForError } from '@sentry-internal/test-utils';
+
+test.describe('server-side errors', () => {
+ test('captures SSR error', async ({ page }) => {
+ const errorEventPromise = waitForError('astro-4', errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === "Cannot read properties of undefined (reading 'x')";
+ });
+
+ await page.goto('/ssr-error');
+
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent).toMatchObject({
+ contexts: {
+ app: expect.any(Object),
+ cloud_resource: expect.any(Object),
+ culture: expect.any(Object),
+ device: expect.any(Object),
+ os: expect.any(Object),
+ runtime: expect.any(Object),
+ trace: {
+ span_id: '', //TODO: This is a bug! We should expect.stringMatching(/[a-f0-9]{16}/) instead of ''
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ },
+ },
+ environment: 'qa',
+ event_id: expect.stringMatching(/[a-f0-9]{32}/),
+ exception: {
+ values: [
+ {
+ mechanism: {
+ data: {
+ function: 'astroMiddleware',
+ },
+ handled: false,
+ type: 'astro',
+ },
+ stacktrace: expect.any(Object),
+ type: 'TypeError',
+ value: "Cannot read properties of undefined (reading 'x')",
+ },
+ ],
+ },
+ platform: 'node',
+ request: {
+ cookies: {},
+ headers: expect.objectContaining({
+ // demonstrates that requestData integration is getting data
+ host: 'localhost:3030',
+ 'user-agent': expect.any(String),
+ }),
+ method: 'GET',
+ url: expect.stringContaining('/ssr-error'),
+ },
+ sdk: {
+ integrations: expect.any(Array),
+ name: 'sentry.javascript.astro',
+ packages: expect.any(Array),
+ version: expect.any(String),
+ },
+ server_name: expect.any(String),
+ timestamp: expect.any(Number),
+ transaction: 'GET /ssr-error',
+ });
+ });
+
+ test('captures endpoint error', async ({ page }) => {
+ const errorEventPromise = waitForError('astro-4', errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === 'Endpoint Error';
+ });
+
+ await page.goto('/endpoint-error');
+ await page.getByText('Get Data').click();
+
+ const errorEvent = await errorEventPromise;
+
+ expect(errorEvent).toMatchObject({
+ contexts: {
+ trace: {
+ parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ },
+ },
+ exception: {
+ values: [
+ {
+ mechanism: {
+ data: {
+ function: 'astroMiddleware',
+ },
+ handled: false,
+ type: 'astro',
+ },
+ stacktrace: expect.any(Object),
+ type: 'Error',
+ value: 'Endpoint Error',
+ },
+ ],
+ },
+ platform: 'node',
+ request: {
+ cookies: {},
+ headers: expect.objectContaining({
+ accept: expect.any(String),
+ }),
+ method: 'GET',
+ query_string: 'error=1',
+ url: expect.stringContaining('endpoint-error/api?error=1'),
+ },
+ transaction: 'GET /endpoint-error/api',
+ });
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.dynamic.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.dynamic.test.ts
new file mode 100644
index 000000000000..9a295f677d96
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.dynamic.test.ts
@@ -0,0 +1,123 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/test-utils';
+
+test.describe('tracing in dynamically rendered (ssr) routes', () => {
+ test('sends server and client pageload spans with the same trace id', async ({ page }) => {
+ const clientPageloadTxnPromise = waitForTransaction('astro-4', txnEvent => {
+ return txnEvent?.transaction === '/test-ssr';
+ });
+
+ const serverPageRequestTxnPromise = waitForTransaction('astro-4', txnEvent => {
+ return txnEvent?.transaction === 'GET /test-ssr';
+ });
+
+ await page.goto('/test-ssr');
+
+ const clientPageloadTxn = await clientPageloadTxnPromise;
+ const serverPageRequestTxn = await serverPageRequestTxnPromise;
+
+ const clientPageloadTraceId = clientPageloadTxn.contexts?.trace?.trace_id;
+ const clientPageloadParentSpanId = clientPageloadTxn.contexts?.trace?.parent_span_id;
+
+ const serverPageRequestTraceId = serverPageRequestTxn.contexts?.trace?.trace_id;
+ const serverPageloadSpanId = serverPageRequestTxn.contexts?.trace?.span_id;
+
+ expect(clientPageloadTraceId).toEqual(serverPageRequestTraceId);
+ expect(clientPageloadParentSpanId).toEqual(serverPageloadSpanId);
+
+ expect(clientPageloadTxn).toMatchObject({
+ contexts: {
+ trace: {
+ data: expect.objectContaining({
+ '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}/),
+ parent_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: expect.any(Object),
+ platform: 'javascript',
+ request: expect.any(Object),
+ sdk: {
+ integrations: expect.any(Array),
+ name: 'sentry.javascript.astro',
+ packages: expect.any(Array),
+ version: expect.any(String),
+ },
+ spans: expect.any(Array),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ transaction: '/test-ssr',
+ transaction_info: {
+ source: 'url',
+ },
+ type: 'transaction',
+ });
+
+ expect(serverPageRequestTxn).toMatchObject({
+ breadcrumbs: expect.any(Array),
+ contexts: {
+ app: expect.any(Object),
+ cloud_resource: expect.any(Object),
+ culture: expect.any(Object),
+ device: expect.any(Object),
+ os: expect.any(Object),
+ otel: expect.any(Object),
+ runtime: expect.any(Object),
+ trace: {
+ data: {
+ 'http.response.status_code': 200,
+ method: 'GET',
+ 'sentry.op': 'http.server',
+ 'sentry.origin': 'auto.http.astro',
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'route',
+ url: expect.stringContaining('/test-ssr'),
+ },
+ op: 'http.server',
+ origin: 'auto.http.astro',
+ status: 'ok',
+ 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}/),
+ platform: 'node',
+ request: {
+ cookies: {},
+ headers: expect.objectContaining({
+ // demonstrates that request data integration can extract headers
+ accept: expect.any(String),
+ 'accept-encoding': expect.any(String),
+ 'user-agent': expect.any(String),
+ }),
+ method: 'GET',
+ url: expect.stringContaining('/test-ssr'),
+ },
+ sdk: {
+ integrations: expect.any(Array),
+ name: 'sentry.javascript.astro',
+ packages: expect.any(Array),
+ version: expect.any(String),
+ },
+ server_name: expect.any(String),
+ spans: expect.any(Array),
+ start_timestamp: expect.any(Number),
+ timestamp: expect.any(Number),
+ transaction: 'GET /test-ssr',
+ transaction_info: {
+ source: 'route',
+ },
+ type: 'transaction',
+ });
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.static.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.static.test.ts
new file mode 100644
index 000000000000..8817b2b22aa7
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.static.test.ts
@@ -0,0 +1,62 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/test-utils';
+
+test.describe('tracing in static/pre-rendered routes', () => {
+ test('only sends client pageload span with traceId from pre-rendered tags', async ({ page }) => {
+ const clientPageloadTxnPromise = waitForTransaction('astro-4', txnEvent => {
+ return txnEvent?.transaction === '/test-static';
+ });
+
+ waitForTransaction('astro-4', evt => {
+ if (evt.platform !== 'javascript') {
+ throw new Error('Server transaction should not be sent');
+ }
+ return false;
+ });
+
+ await page.goto('/test-static');
+
+ const clientPageloadTxn = await clientPageloadTxnPromise;
+
+ const clientPageloadTraceId = clientPageloadTxn.contexts?.trace?.trace_id;
+ const clientPageloadParentSpanId = clientPageloadTxn.contexts?.trace?.parent_span_id;
+
+ const sentryTraceMetaTagContent = await page.locator('meta[name="sentry-trace"]').getAttribute('content');
+ const baggageMetaTagContent = await page.locator('meta[name="baggage"]').getAttribute('content');
+
+ const [metaTraceId, metaParentSpanId, metaSampled] = sentryTraceMetaTagContent?.split('-') || [];
+
+ expect(clientPageloadTraceId).toMatch(/[a-f0-9]{32}/);
+ expect(clientPageloadParentSpanId).toMatch(/[a-f0-9]{16}/);
+ expect(metaSampled).toBe('1');
+
+ expect(clientPageloadTxn).toMatchObject({
+ contexts: {
+ trace: {
+ data: expect.objectContaining({
+ 'sentry.op': 'pageload',
+ 'sentry.origin': 'auto.pageload.browser',
+ 'sentry.sample_rate': 1,
+ 'sentry.source': 'url',
+ }),
+ op: 'pageload',
+ origin: 'auto.pageload.browser',
+ parent_span_id: metaParentSpanId,
+ span_id: expect.stringMatching(/[a-f0-9]{16}/),
+ trace_id: metaTraceId,
+ },
+ },
+ platform: 'javascript',
+ transaction: '/test-static',
+ transaction_info: {
+ source: 'url',
+ },
+ type: 'transaction',
+ });
+
+ expect(baggageMetaTagContent).toContain('sentry-transaction=GET%20%2Ftest-static%2F'); // URL-encoded for 'GET /test-static/'
+ expect(baggageMetaTagContent).toContain('sentry-sampled=true');
+
+ await page.waitForTimeout(1000); // wait another sec to ensure no server transaction is sent
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/astro-4/tsconfig.json b/dev-packages/e2e-tests/test-applications/astro-4/tsconfig.json
new file mode 100644
index 000000000000..77da9dd00982
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/astro-4/tsconfig.json
@@ -0,0 +1,3 @@
+{
+ "extends": "astro/tsconfigs/strict"
+}
\ No newline at end of file
diff --git a/packages/astro/src/server/middleware.ts b/packages/astro/src/server/middleware.ts
index 3752bd30d448..95d099ff0526 100644
--- a/packages/astro/src/server/middleware.ts
+++ b/packages/astro/src/server/middleware.ts
@@ -147,7 +147,7 @@ async function instrumentRequest(
async span => {
const originalResponse = await next();
- if (span && originalResponse.status) {
+ if (originalResponse.status) {
setHttpStatus(span, originalResponse.status);
}