From cd2a8464f5ce550e90c3c6fec253582130fda826 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Fri, 31 Jul 2026 14:06:44 +0200 Subject: [PATCH 1/3] migrate e2e --- .../nuxt-4/docker-compose.yml | 31 ++++++ .../test-applications/nuxt-4/global-setup.mjs | 14 +++ .../nuxt-4/global-teardown.mjs | 12 +++ .../test-applications/nuxt-4/package.json | 2 + .../nuxt-4/playwright.config.ts | 6 +- .../nuxt-4/server/api/db-ioredis.ts | 17 ++++ .../nuxt-4/server/api/db-mysql.ts | 27 +++++ .../nuxt-4/tests/build-injection.test.ts | 42 ++++++++ .../nuxt-4/tests/db-drivers.test.ts | 99 +++++++++++++++++++ packages/nuxt/test/vite/orchestrion.test.ts | 12 +++ 10 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4/docker-compose.yml create mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4/global-setup.mjs create mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4/global-teardown.mjs create mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4/server/api/db-ioredis.ts create mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4/server/api/db-mysql.ts create mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4/tests/build-injection.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4/tests/db-drivers.test.ts diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/docker-compose.yml b/dev-packages/e2e-tests/test-applications/nuxt-4/docker-compose.yml new file mode 100644 index 000000000000..a50a0097e5ae --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/docker-compose.yml @@ -0,0 +1,31 @@ +services: + db: + image: mysql:8.0 + restart: always + container_name: e2e-tests-nuxt-4-mysql + # The `mysql` 2.x driver doesn't speak MySQL 8's default + # `caching_sha2_password` auth, so force the legacy plugin. + command: ['--default-authentication-plugin=mysql_native_password'] + ports: + - '3306:3306' + environment: + MYSQL_ROOT_PASSWORD: docker + healthcheck: + test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 -uroot -pdocker'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 10s + + redis: + image: redis:7 + restart: always + container_name: e2e-tests-nuxt-4-redis + ports: + - '6379:6379' + healthcheck: + test: ['CMD', 'redis-cli', 'ping'] + interval: 2s + timeout: 3s + retries: 30 + start_period: 5s diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/global-setup.mjs b/dev-packages/e2e-tests/test-applications/nuxt-4/global-setup.mjs new file mode 100644 index 000000000000..cb48d539c466 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/global-setup.mjs @@ -0,0 +1,14 @@ +import { execSync } from 'child_process'; +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default async function globalSetup() { + // Start MySQL + Redis via Docker Compose. `--wait` blocks until the + // healthchecks in docker-compose.yml pass, so the app can connect immediately. + execSync('docker compose up -d --wait', { + cwd: __dirname, + stdio: 'inherit', + }); +} diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/global-teardown.mjs b/dev-packages/e2e-tests/test-applications/nuxt-4/global-teardown.mjs new file mode 100644 index 000000000000..2742279431ad --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/global-teardown.mjs @@ -0,0 +1,12 @@ +import { execSync } from 'child_process'; +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default async function globalTeardown() { + execSync('docker compose down --volumes', { + cwd: __dirname, + stdio: 'inherit', + }); +} diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/package.json b/dev-packages/e2e-tests/test-applications/nuxt-4/package.json index 02477111483d..e66ce2bd3610 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4/package.json +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/package.json @@ -20,6 +20,8 @@ "dependencies": { "@pinia/nuxt": "^0.5.5", "@sentry/nuxt": "file:../../packed/sentry-nuxt-packed.tgz", + "ioredis": "5.10.1", + "mysql": "^2.18.1", "nuxt": "^4.1.2" }, "devDependencies": { diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/playwright.config.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/playwright.config.ts index b86690ca086c..0b61db7cde11 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4/playwright.config.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/playwright.config.ts @@ -22,4 +22,8 @@ const config = getPlaywrightConfig({ startCommand: getStartCommand(), }); -export default config; +export default { + ...config, + globalSetup: './global-setup.mjs', + globalTeardown: './global-teardown.mjs', +}; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/server/api/db-ioredis.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/server/api/db-ioredis.ts new file mode 100644 index 000000000000..d623fa65a27e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/server/api/db-ioredis.ts @@ -0,0 +1,17 @@ +import { defineEventHandler } from '#imports'; +import Redis from 'ioredis'; + +export default defineEventHandler(async () => { + const redis = new Redis({ + // Don't keep retrying forever if Redis goes away (e.g. on test teardown) + maxRetriesPerRequest: 1, + retryStrategy: () => null, + }); + + try { + await redis.set('test-key', 'test-value'); + return await redis.get('test-key'); + } finally { + redis.disconnect(); + } +}); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/server/api/db-mysql.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/server/api/db-mysql.ts new file mode 100644 index 000000000000..5c05d2062105 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/server/api/db-mysql.ts @@ -0,0 +1,27 @@ +import { defineEventHandler } from '#imports'; +import mysql from 'mysql'; + +const connection = mysql.createConnection({ + user: 'root', + password: 'docker', +}); + +export default defineEventHandler(() => { + return new Promise((resolve, reject) => { + connection.query('SELECT 1 + 1 AS solution', error => { + if (error) { + reject(error); + return; + } + + connection.query('SELECT NOW()', ['1', '2'], nestedError => { + if (nestedError) { + reject(nestedError); + return; + } + + resolve({ status: 'ok' }); + }); + }); + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/build-injection.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/build-injection.test.ts new file mode 100644 index 000000000000..3cc7b960feee --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/build-injection.test.ts @@ -0,0 +1,42 @@ +import { readFileSync, readdirSync } from 'node:fs'; +import path from 'node:path'; +import { expect, test } from '@playwright/test'; + +function readServerBundle(): string { + const serverDir = path.join(process.cwd(), '.output/server'); + return readdirSync(serverDir, { recursive: true }) + .filter(file => typeof file === 'string' && file.endsWith('.mjs')) + .map(file => readFileSync(path.join(serverDir, file), 'utf8')) + .join('\n'); +} + +function readClientBundle(): string { + const serverDir = path.join(process.cwd(), '.output/public'); + return readdirSync(serverDir, { recursive: true }) + .filter(file => typeof file === 'string' && file.endsWith('.js')) + .map(file => readFileSync(path.join(serverDir, file), 'utf8')) + .join('\n'); +} + +test.describe('Orchestrion build-time injection', () => { + const serverBundle = readServerBundle(); + const clientBundle = readClientBundle(); + + test('force-bundles instrumented dependencies', () => { + expect(serverBundle).not.toMatch(/(from\s*["']mysql["']|require\(["']mysql["']\))/); + expect(serverBundle).not.toMatch(/(from\s*["']ioredis["']|require\(["']ioredis["']\))/); + expect(serverBundle).not.toMatch(/(from\s*["']standard-as-callback["']|require\(["']standard-as-callback["']\))/); + }); + + test('injects diagnostics-channel publishers into the server build', () => { + expect(serverBundle).toContain('__SENTRY_ORCHESTRION__'); + expect(serverBundle).toMatch(/tracingChannel\(["']orchestrion:mysql:query["']\)/); + expect(serverBundle).toMatch(/tracingChannel\(["']orchestrion:ioredis:command["']\)/); + expect(serverBundle).toMatch(/tracingChannel\(["']orchestrion:ioredis:connect["']\)/); + }); + + test('does not inject diagnostics-channel publishers into the client build', () => { + expect(clientBundle).not.toContain('__SENTRY_ORCHESTRION__'); + expect(clientBundle).not.toMatch(/orchestrion:/); + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/db-drivers.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/db-drivers.test.ts new file mode 100644 index 000000000000..464850a116dd --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/db-drivers.test.ts @@ -0,0 +1,99 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +// The Nuxt module auto-wires the orchestrion build-time transform, which injects +// `diagnostics_channel` publishers into these drivers as Nitro bundles them. That +// only happens in the production build, so these tests are excluded from the +// `test:dev` pass (which filters to `environment`). +test('Instruments ioredis automatically', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('nuxt-4', transactionEvent => { + return ( + transactionEvent.contexts?.trace?.op === 'http.server' && transactionEvent.transaction === 'GET /api/db-ioredis' + ); + }); + + const response = await fetch(`${baseURL}/api/db-ioredis`); + expect(response.status).toBe(200); + expect(await response.text()).toBe('test-value'); + + const transactionEvent = await transactionEventPromise; + + expect(transactionEvent.contexts?.trace?.op).toEqual('http.server'); + expect(transactionEvent.transaction).toEqual('GET /api/db-ioredis'); + + const spans = transactionEvent.spans || []; + + expect(spans).toContainEqual( + expect.objectContaining({ + op: 'db', + origin: 'auto.db.redis', + description: 'set test-key [1 other arguments]', + status: 'ok', + data: expect.objectContaining({ + 'db.system': 'redis', + 'db.statement': 'set test-key [1 other arguments]', + }), + }), + ); + expect(spans).toContainEqual( + expect.objectContaining({ + op: 'db', + origin: 'auto.db.redis', + description: 'get test-key', + status: 'ok', + data: expect.objectContaining({ + 'db.system': 'redis', + 'db.statement': 'get test-key', + }), + }), + ); +}); + +test('Instruments mysql automatically', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('nuxt-4', transactionEvent => { + return ( + transactionEvent.contexts?.trace?.op === 'http.server' && transactionEvent.transaction === 'GET /api/db-mysql' + ); + }); + + const response = await fetch(`${baseURL}/api/db-mysql`); + expect(response.status).toBe(200); + await expect(response.json()).resolves.toEqual({ status: 'ok' }); + + const transactionEvent = await transactionEventPromise; + + const spans = transactionEvent.spans || []; + + expect(spans).toContainEqual( + expect.objectContaining({ + op: 'db', + origin: 'auto.db.mysql', + description: 'SELECT 1 + 1 AS solution', + status: 'ok', + data: expect.objectContaining({ + 'db.system': 'mysql', + 'db.statement': 'SELECT 1 + 1 AS solution', + 'db.user': 'root', + 'db.connection_string': expect.any(String), + 'net.peer.name': expect.any(String), + 'net.peer.port': 3306, + }), + }), + ); + expect(spans).toContainEqual( + expect.objectContaining({ + op: 'db', + origin: 'auto.db.mysql', + description: 'SELECT NOW()', + status: 'ok', + data: expect.objectContaining({ + 'db.system': 'mysql', + 'db.statement': 'SELECT NOW()', + 'db.user': 'root', + 'db.connection_string': expect.any(String), + 'net.peer.name': expect.any(String), + 'net.peer.port': 3306, + }), + }), + ); +}); diff --git a/packages/nuxt/test/vite/orchestrion.test.ts b/packages/nuxt/test/vite/orchestrion.test.ts index 1d0f8d105627..4b1a79fce4b0 100644 --- a/packages/nuxt/test/vite/orchestrion.test.ts +++ b/packages/nuxt/test/vite/orchestrion.test.ts @@ -70,6 +70,18 @@ describe('setupOrchestrion', () => { }); }); + it('does not change Nitro configuration when `buildTimeInstrumentation` is `false`', async () => { + const { setupOrchestrion } = await import('../../src/vite/orchestrion'); + const mockNuxt = createMockNuxt(); + const nitroConfig = {}; + + setupOrchestrion(mockNuxt as unknown as Nuxt, false); + await mockNuxt.triggerHook('nitro:config', nitroConfig); + + expect(mockSentryOrchestrionPlugin).not.toHaveBeenCalled(); + expect(nitroConfig).toEqual({}); + }); + it('does not change Nitro configuration in prepare mode', async () => { const { setupOrchestrion } = await import('../../src/vite/orchestrion'); const mockNuxt = createMockNuxt({ _prepare: true }); From a21c81817f3aeaf9d7b9e0b4b5d8a4be49b560d7 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Fri, 31 Jul 2026 14:07:00 +0200 Subject: [PATCH 2/3] delete old e2e --- .../nuxt-4-orchestrion/.gitignore | 24 ----- .../nuxt-4-orchestrion/app/app.vue | 6 -- .../nuxt-4-orchestrion/docker-compose.yml | 31 ------ .../nuxt-4-orchestrion/global-setup.mjs | 14 --- .../nuxt-4-orchestrion/global-teardown.mjs | 12 --- .../nuxt-4-orchestrion/nuxt.config.ts | 22 ---- .../nuxt-4-orchestrion/package.json | 35 ------- .../nuxt-4-orchestrion/playwright.config.ts | 11 -- .../nuxt-4-orchestrion/public/favicon.ico | Bin 4286 -> 0 bytes .../nuxt-4-orchestrion/public/robots.txt | 2 - .../sentry.client.config.ts | 9 -- .../sentry.server.config.ts | 9 -- .../server/api/db-ioredis.ts | 17 ---- .../nuxt-4-orchestrion/server/api/db-mysql.ts | 27 ----- .../nuxt-4-orchestrion/start-event-proxy.mjs | 6 -- .../tests/build-injection.test.ts | 42 -------- .../nuxt-4-orchestrion/tests/db.test.ts | 95 ------------------ .../nuxt-4-orchestrion/tsconfig.json | 18 ---- 18 files changed, 380 deletions(-) delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/.gitignore delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/app/app.vue delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/docker-compose.yml delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/global-setup.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/global-teardown.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/nuxt.config.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/package.json delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/playwright.config.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/public/favicon.ico delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/public/robots.txt delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/sentry.client.config.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/sentry.server.config.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/server/api/db-ioredis.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/server/api/db-mysql.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/start-event-proxy.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/tests/build-injection.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/tests/db.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/tsconfig.json diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/.gitignore b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/.gitignore deleted file mode 100644 index 4a7f73a2ed0d..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Nuxt dev/build outputs -.output -.data -.nuxt -.nitro -.cache -dist - -# Node dependencies -node_modules - -# Logs -logs -*.log - -# Misc -.DS_Store -.fleet -.idea - -# Local env files -.env -.env.* -!.env.example diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/app/app.vue b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/app/app.vue deleted file mode 100644 index 09f935bbb637..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/app/app.vue +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/docker-compose.yml b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/docker-compose.yml deleted file mode 100644 index f8191b69acad..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/docker-compose.yml +++ /dev/null @@ -1,31 +0,0 @@ -services: - db: - image: mysql:8.0 - restart: always - container_name: e2e-tests-nuxt-4-orchestrion-mysql - # The `mysql` 2.x driver doesn't speak MySQL 8's default - # `caching_sha2_password` auth, so force the legacy plugin. - command: ['--default-authentication-plugin=mysql_native_password'] - ports: - - '3306:3306' - environment: - MYSQL_ROOT_PASSWORD: docker - healthcheck: - test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 -uroot -pdocker'] - interval: 2s - timeout: 3s - retries: 30 - start_period: 10s - - redis: - image: redis:7 - restart: always - container_name: e2e-tests-nuxt-4-orchestrion-redis - ports: - - '6379:6379' - healthcheck: - test: ['CMD', 'redis-cli', 'ping'] - interval: 2s - timeout: 3s - retries: 30 - start_period: 5s diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/global-setup.mjs b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/global-setup.mjs deleted file mode 100644 index 634e34824a2f..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/global-setup.mjs +++ /dev/null @@ -1,14 +0,0 @@ -import { execSync } from 'child_process'; -import { dirname } from 'path'; -import { fileURLToPath } from 'url'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export default async function globalSetup() { - // Start MySQL via Docker Compose. `--wait` blocks until the healthcheck - // in docker-compose.yml passes, so the app can connect immediately. - execSync('docker compose up -d --wait', { - cwd: __dirname, - stdio: 'inherit', - }); -} diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/global-teardown.mjs b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/global-teardown.mjs deleted file mode 100644 index 2742279431ad..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/global-teardown.mjs +++ /dev/null @@ -1,12 +0,0 @@ -import { execSync } from 'child_process'; -import { dirname } from 'path'; -import { fileURLToPath } from 'url'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export default async function globalTeardown() { - execSync('docker compose down --volumes', { - cwd: __dirname, - stdio: 'inherit', - }); -} diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/nuxt.config.ts b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/nuxt.config.ts deleted file mode 100644 index 8e7b7a15eedb..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/nuxt.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -// https://nuxt.com/docs/api/configuration/nuxt-config -export default defineNuxtConfig({ - compatibilityDate: '2025-07-15', - devtools: { enabled: true }, - - modules: ['@sentry/nuxt/module'], - - sentry: { - _experimental: { - useDiagnosticsChannelInjection: true, - }, - autoInjectServerSentry: 'top-level-import', - }, - - runtimeConfig: { - public: { - sentry: { - dsn: 'https://public@dsn.ingest.sentry.io/1337', - }, - }, - }, -}); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/package.json b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/package.json deleted file mode 100644 index ca4c3a887d9b..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "nuxt-4-orchestrion", - "type": "module", - "private": true, - "scripts": { - "build": "nuxt build", - "dev": "nuxt dev", - "generate": "nuxt generate", - "preview": "nuxt preview", - "start": "node .output/server/index.mjs", - "clean": "npx nuxi cleanup", - "test": "playwright test", - "test:prod": "TEST_ENV=production playwright test", - "test:build": "pnpm install && pnpm build", - "test:build-canary": "pnpm add nuxt@npm:nuxt-nightly@latest && pnpm add nitropack@npm:nitropack-nightly@latest && pnpm install --force && pnpm build", - "test:assert": "pnpm test:prod" - }, - "//": "Need to use ioredis 5.10.1 because that's the last version before they support tracing channels", - "dependencies": { - "@sentry/nuxt": "file:../../packed/sentry-nuxt-packed.tgz", - "ioredis": "5.10.1", - "mysql": "^2.18.1", - "nuxt": "^4.4.8", - "vue": "^3.5.38", - "vue-router": "^5.1.0" - }, - "devDependencies": { - "@playwright/test": "~1.56.0", - "@sentry-internal/test-utils": "link:../../../test-utils" - }, - "volta": { - "extends": "../../package.json", - "node": "22.20.0" - } -} diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/playwright.config.ts b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/playwright.config.ts deleted file mode 100644 index 047fc701fa50..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/playwright.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getPlaywrightConfig } from '@sentry-internal/test-utils'; - -const config = getPlaywrightConfig({ - startCommand: 'pnpm start', -}); - -export default { - ...config, - globalSetup: './global-setup.mjs', - globalTeardown: './global-teardown.mjs', -}; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/public/favicon.ico b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/public/favicon.ico deleted file mode 100644 index 18993ad91cfd43e03b074dd0b5cc3f37ab38e49c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmeHLOKuuL5PjK%MHWVi6lD zOGiREbCw`xmFozJ^aNatJY>w+g ze6a2@u~m#^BZm@8wco9#Crlli0uLb^3E$t2-WIc^#(?t)*@`UpuofJ(Uyh@F>b3Ph z$D^m8Xq~pTkGJ4Q`Q2)te3mgkWYZ^Ijq|hkiP^9`De={bQQ%heZC$QU2UpP(-tbl8 zPWD2abEew;oat@w`uP3J^YpsgT%~jT(Dk%oU}sa$7|n6hBjDj`+I;RX(>)%lm_7N{+B7Mu%H?422lE%MBJH!!YTN2oT7xr>>N-8OF$C&qU^ z>vLsa{$0X%q1fjOe3P1mCv#lN{xQ4_*HCSAZjTb1`}mlc+9rl8$B3OP%VT@mch_~G z7Y+4b{r>9e=M+7vSI;BgB?ryZDY4m>&wcHSn81VH1N~`0gvwH{ z8dv#hG|OK`>1;j7tM#B)Z7zDN?{6=dUal}$e { - const redis = new Redis({ - // Don't keep retrying forever if Redis goes away (e.g. on test teardown) - maxRetriesPerRequest: 1, - retryStrategy: () => null, - }); - - try { - await redis.set('test-key', 'test-value'); - return await redis.get('test-key'); - } finally { - redis.disconnect(); - } -}); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/server/api/db-mysql.ts b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/server/api/db-mysql.ts deleted file mode 100644 index 5c05d2062105..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/server/api/db-mysql.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { defineEventHandler } from '#imports'; -import mysql from 'mysql'; - -const connection = mysql.createConnection({ - user: 'root', - password: 'docker', -}); - -export default defineEventHandler(() => { - return new Promise((resolve, reject) => { - connection.query('SELECT 1 + 1 AS solution', error => { - if (error) { - reject(error); - return; - } - - connection.query('SELECT NOW()', ['1', '2'], nestedError => { - if (nestedError) { - reject(nestedError); - return; - } - - resolve({ status: 'ok' }); - }); - }); - }); -}); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/start-event-proxy.mjs deleted file mode 100644 index 68e43bd9598c..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/start-event-proxy.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import { startEventProxyServer } from '@sentry-internal/test-utils'; - -startEventProxyServer({ - port: 3031, - proxyServerName: 'nuxt-4-orchestrion', -}); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/tests/build-injection.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/tests/build-injection.test.ts deleted file mode 100644 index 3cc7b960feee..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/tests/build-injection.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { readFileSync, readdirSync } from 'node:fs'; -import path from 'node:path'; -import { expect, test } from '@playwright/test'; - -function readServerBundle(): string { - const serverDir = path.join(process.cwd(), '.output/server'); - return readdirSync(serverDir, { recursive: true }) - .filter(file => typeof file === 'string' && file.endsWith('.mjs')) - .map(file => readFileSync(path.join(serverDir, file), 'utf8')) - .join('\n'); -} - -function readClientBundle(): string { - const serverDir = path.join(process.cwd(), '.output/public'); - return readdirSync(serverDir, { recursive: true }) - .filter(file => typeof file === 'string' && file.endsWith('.js')) - .map(file => readFileSync(path.join(serverDir, file), 'utf8')) - .join('\n'); -} - -test.describe('Orchestrion build-time injection', () => { - const serverBundle = readServerBundle(); - const clientBundle = readClientBundle(); - - test('force-bundles instrumented dependencies', () => { - expect(serverBundle).not.toMatch(/(from\s*["']mysql["']|require\(["']mysql["']\))/); - expect(serverBundle).not.toMatch(/(from\s*["']ioredis["']|require\(["']ioredis["']\))/); - expect(serverBundle).not.toMatch(/(from\s*["']standard-as-callback["']|require\(["']standard-as-callback["']\))/); - }); - - test('injects diagnostics-channel publishers into the server build', () => { - expect(serverBundle).toContain('__SENTRY_ORCHESTRION__'); - expect(serverBundle).toMatch(/tracingChannel\(["']orchestrion:mysql:query["']\)/); - expect(serverBundle).toMatch(/tracingChannel\(["']orchestrion:ioredis:command["']\)/); - expect(serverBundle).toMatch(/tracingChannel\(["']orchestrion:ioredis:connect["']\)/); - }); - - test('does not inject diagnostics-channel publishers into the client build', () => { - expect(clientBundle).not.toContain('__SENTRY_ORCHESTRION__'); - expect(clientBundle).not.toMatch(/orchestrion:/); - }); -}); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/tests/db.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/tests/db.test.ts deleted file mode 100644 index 0e5d5c41bdd6..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/tests/db.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; - -test('Instruments ioredis automatically', async ({ baseURL }) => { - const transactionEventPromise = waitForTransaction('nuxt-4-orchestrion', transactionEvent => { - return ( - transactionEvent.contexts?.trace?.op === 'http.server' && transactionEvent.transaction === 'GET /api/db-ioredis' - ); - }); - - const response = await fetch(`${baseURL}/api/db-ioredis`); - expect(response.status).toBe(200); - expect(await response.text()).toBe('test-value'); - - const transactionEvent = await transactionEventPromise; - - expect(transactionEvent.contexts?.trace?.op).toEqual('http.server'); - expect(transactionEvent.transaction).toEqual('GET /api/db-ioredis'); - - const spans = transactionEvent.spans || []; - - expect(spans).toContainEqual( - expect.objectContaining({ - op: 'db', - origin: 'auto.db.redis', - description: 'set test-key [1 other arguments]', - status: 'ok', - data: expect.objectContaining({ - 'db.system': 'redis', - 'db.statement': 'set test-key [1 other arguments]', - }), - }), - ); - expect(spans).toContainEqual( - expect.objectContaining({ - op: 'db', - origin: 'auto.db.redis', - description: 'get test-key', - status: 'ok', - data: expect.objectContaining({ - 'db.system': 'redis', - 'db.statement': 'get test-key', - }), - }), - ); -}); - -test('Instruments mysql automatically', async ({ baseURL }) => { - const transactionEventPromise = waitForTransaction('nuxt-4-orchestrion', transactionEvent => { - return ( - transactionEvent.contexts?.trace?.op === 'http.server' && transactionEvent.transaction === 'GET /api/db-mysql' - ); - }); - - const response = await fetch(`${baseURL}/api/db-mysql`); - expect(response.status).toBe(200); - await expect(response.json()).resolves.toEqual({ status: 'ok' }); - - const transactionEvent = await transactionEventPromise; - - const spans = transactionEvent.spans || []; - - expect(spans).toContainEqual( - expect.objectContaining({ - op: 'db', - origin: 'auto.db.mysql', - description: 'SELECT 1 + 1 AS solution', - status: 'ok', - data: expect.objectContaining({ - 'db.system': 'mysql', - 'db.statement': 'SELECT 1 + 1 AS solution', - 'db.user': 'root', - 'db.connection_string': expect.any(String), - 'net.peer.name': expect.any(String), - 'net.peer.port': 3306, - }), - }), - ); - expect(spans).toContainEqual( - expect.objectContaining({ - op: 'db', - origin: 'auto.db.mysql', - description: 'SELECT NOW()', - status: 'ok', - data: expect.objectContaining({ - 'db.system': 'mysql', - 'db.statement': 'SELECT NOW()', - 'db.user': 'root', - 'db.connection_string': expect.any(String), - 'net.peer.name': expect.any(String), - 'net.peer.port': 3306, - }), - }), - ); -}); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/tsconfig.json b/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/tsconfig.json deleted file mode 100644 index 307b2134b94b..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-orchestrion/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - // https://nuxt.com/docs/guide/concepts/typescript - "files": [], - "references": [ - { - "path": "./.nuxt/tsconfig.app.json" - }, - { - "path": "./.nuxt/tsconfig.server.json" - }, - { - "path": "./.nuxt/tsconfig.shared.json" - }, - { - "path": "./.nuxt/tsconfig.node.json" - } - ] -} From b4282118a37af36640a92c0c609d2f0cdf039d99 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Fri, 31 Jul 2026 14:07:08 +0200 Subject: [PATCH 3/3] sdk changes --- packages/nuxt/src/common/types.ts | 17 ----------------- packages/nuxt/src/module.ts | 4 +--- packages/nuxt/src/vite/orchestrion.ts | 6 +++++- packages/nuxt/test/vite/buildOptions.test-d.ts | 4 +--- 4 files changed, 7 insertions(+), 24 deletions(-) diff --git a/packages/nuxt/src/common/types.ts b/packages/nuxt/src/common/types.ts index afcf0a327d52..9bd61aece2cc 100644 --- a/packages/nuxt/src/common/types.ts +++ b/packages/nuxt/src/common/types.ts @@ -183,23 +183,6 @@ export type SentryNuxtModuleOptions = BuildTimeOptionsBase & { */ enabled?: boolean; - // todo(v11): Update this JSDoc (and remove from experimental) - /** - * Experimental build-time options that may change or be removed without notice. - */ - _experimental?: { - /** - * Enables build-time diagnostics-channel instrumentation for supported dependencies bundled into the Nitro server. - * - * 1. Remove `--import ./.output/server/sentry.server.config.mjs` from your `start` script - * 2. Add `sentry.autoInjectServerSentry: 'top-level-import'` in `nuxt.config.ts` so Sentry's server configuration is automatically imported - * - * @default false - * @experimental May change or be removed in any release. - */ - useDiagnosticsChannelInjection?: boolean; - }; - /** * Options for the Sentry Vite plugin to customize the source maps upload process. * diff --git a/packages/nuxt/src/module.ts b/packages/nuxt/src/module.ts index 4cf39d16cdbc..fd3e874d214d 100644 --- a/packages/nuxt/src/module.ts +++ b/packages/nuxt/src/module.ts @@ -85,9 +85,7 @@ export default defineNuxtModule({ const isMinNuxtV4 = nuxtMajor >= 4; if (serverConfigFile) { - if (moduleOptions._experimental?.useDiagnosticsChannelInjection) { - setupOrchestrion(nuxt); - } + setupOrchestrion(nuxt, moduleOptions.buildTimeInstrumentation); if (isNitroV3) { addServerPlugin(moduleDirResolver.resolve('./runtime/plugins/handler.server')); diff --git a/packages/nuxt/src/vite/orchestrion.ts b/packages/nuxt/src/vite/orchestrion.ts index 5bc3d3be1a12..db367e295c59 100644 --- a/packages/nuxt/src/vite/orchestrion.ts +++ b/packages/nuxt/src/vite/orchestrion.ts @@ -11,7 +11,11 @@ const IORedisDependencies = ['standard-as-callback']; * Configures Nitro to bundle and transform dependencies that publish tracing * events through diagnostics channels. */ -export function setupOrchestrion(nuxt: Nuxt): void { +export function setupOrchestrion(nuxt: Nuxt, buildTimeInstrumentation?: boolean): void { + if (buildTimeInstrumentation === false) { + return; + } + nuxt.hook('nitro:config', (nitroConfig: NitroConfig) => { if (nuxt.options?._prepare) { return; diff --git a/packages/nuxt/test/vite/buildOptions.test-d.ts b/packages/nuxt/test/vite/buildOptions.test-d.ts index 0eca417d46f0..123a55be5239 100644 --- a/packages/nuxt/test/vite/buildOptions.test-d.ts +++ b/packages/nuxt/test/vite/buildOptions.test-d.ts @@ -51,12 +51,10 @@ describe('Sentry Nuxt build-time options type', () => { excludeReplayIframe: true, excludeReplayWorker: true, }, + buildTimeInstrumentation: false, // --- SentryNuxtModuleOptions specific options --- enabled: true, - _experimental: { - useDiagnosticsChannelInjection: true, - }, autoInjectServerSentry: 'experimental_dynamic-import', configDir: '~/custom-config', experimental_entrypointWrappedFunctions: ['default', 'handler', 'server', 'customExport'],