From fa9fa5d81619187122c9778b627b73ea52cba312 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Thu, 12 Sep 2024 16:46:47 +0200 Subject: [PATCH 01/17] test(nuxt): Add tests for Vue component tracking (#13633) --- .../nuxt-3/sentry.client.config.ts | 1 + .../nuxt-3/tests/performance.client.test.ts | 31 ---------- .../nuxt-3/tests/tracing.client.test.ts | 57 +++++++++++++++++++ ....server.test.ts => tracing.server.test.ts} | 0 packages/nuxt/src/index.types.ts | 3 +- 5 files changed, 60 insertions(+), 32 deletions(-) delete mode 100644 dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.client.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/nuxt-3/tests/tracing.client.test.ts rename dev-packages/e2e-tests/test-applications/nuxt-3/tests/{performance.server.test.ts => tracing.server.test.ts} (100%) diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/sentry.client.config.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/sentry.client.config.ts index 5c4e0f892ca8..7547bafa6618 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/sentry.client.config.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/sentry.client.config.ts @@ -6,4 +6,5 @@ Sentry.init({ dsn: useRuntimeConfig().public.sentry.dsn, tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, + trackComponents: true, }); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.client.test.ts deleted file mode 100644 index 66c8c9dfce2d..000000000000 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.client.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { expect, test } from '@nuxt/test-utils/playwright'; -import { waitForTransaction } from '@sentry-internal/test-utils'; - -test('sends a pageload root span with a parameterized URL', async ({ page }) => { - const transactionPromise = waitForTransaction('nuxt-3', async transactionEvent => { - return transactionEvent.transaction === '/test-param/:param()'; - }); - - await page.goto(`/test-param/1234`); - - const rootSpan = await transactionPromise; - - expect(rootSpan).toMatchObject({ - contexts: { - trace: { - data: { - 'sentry.source': 'route', - 'sentry.origin': 'auto.pageload.vue', - 'sentry.op': 'pageload', - 'params.param': '1234', - }, - op: 'pageload', - origin: 'auto.pageload.vue', - }, - }, - transaction: '/test-param/:param()', - transaction_info: { - source: 'route', - }, - }); -}); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/tracing.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/tracing.client.test.ts new file mode 100644 index 000000000000..76b2a9094531 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/tracing.client.test.ts @@ -0,0 +1,57 @@ +import { expect, test } from '@nuxt/test-utils/playwright'; +import { waitForTransaction } from '@sentry-internal/test-utils'; +import type { Span } from '@sentry/nuxt'; + +test('sends a pageload root span with a parameterized URL', async ({ page }) => { + const transactionPromise = waitForTransaction('nuxt-3', async transactionEvent => { + return transactionEvent.transaction === '/test-param/:param()'; + }); + + await page.goto(`/test-param/1234`); + + const rootSpan = await transactionPromise; + + expect(rootSpan).toMatchObject({ + contexts: { + trace: { + data: { + 'sentry.source': 'route', + 'sentry.origin': 'auto.pageload.vue', + 'sentry.op': 'pageload', + 'params.param': '1234', + }, + op: 'pageload', + origin: 'auto.pageload.vue', + }, + }, + transaction: '/test-param/:param()', + transaction_info: { + source: 'route', + }, + }); +}); + +test('sends component tracking spans when `trackComponents` is enabled', async ({ page }) => { + const transactionPromise = waitForTransaction('nuxt-3', async transactionEvent => { + return transactionEvent.transaction === '/client-error'; + }); + + await page.goto(`/client-error`); + + const rootSpan = await transactionPromise; + const errorButtonSpan = rootSpan.spans.find((span: Span) => span.description === 'Vue '); + + const expected = { + data: { 'sentry.origin': 'auto.ui.vue', 'sentry.op': 'ui.vue.mount' }, + description: 'Vue ', + op: 'ui.vue.mount', + parent_span_id: expect.any(String), + span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + trace_id: expect.any(String), + origin: 'auto.ui.vue', + }; + + expect(errorButtonSpan).toMatchObject(expected); +}); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/tracing.server.test.ts similarity index 100% rename from dev-packages/e2e-tests/test-applications/nuxt-3/tests/performance.server.test.ts rename to dev-packages/e2e-tests/test-applications/nuxt-3/tests/tracing.server.test.ts diff --git a/packages/nuxt/src/index.types.ts b/packages/nuxt/src/index.types.ts index d1850eec18ec..614b27bdefe3 100644 --- a/packages/nuxt/src/index.types.ts +++ b/packages/nuxt/src/index.types.ts @@ -1,4 +1,5 @@ import type { Integration, Options, StackParser } from '@sentry/types'; +import type { SentryNuxtClientOptions } from './common/types'; import type * as clientSdk from './index.client'; import type * as serverSdk from './index.server'; @@ -8,7 +9,7 @@ export * from './index.client'; export * from './index.server'; // re-export colliding types -export declare function init(options: Options | clientSdk.BrowserOptions | serverSdk.NodeOptions): void; +export declare function init(options: Options | SentryNuxtClientOptions | serverSdk.NodeOptions): void; export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; export declare const getDefaultIntegrations: (options: Options) => Integration[]; From 1664dc729e8a4bf4a4cc280a2adbb8407101caa9 Mon Sep 17 00:00:00 2001 From: Krystof Woldrich <31292499+krystofwoldrich@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:37:01 +0200 Subject: [PATCH 02/17] fix(normalize): Treat Infinity as NaN both are non-serializable numbers (#13406) RN SDK uses the normalize function before passing data over the RN Bridge, which only accepts serializable data. Infinity causes -> https://github.com/getsentry/sentry-react-native/issues/4024 --- .../suites/public-api/setExtras/consecutive_calls/test.ts | 7 ++++++- packages/utils/src/normalize.ts | 7 ++++--- packages/utils/test/normalize.test.ts | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts b/dev-packages/browser-integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts index 9caae5b0bc7c..555686058366 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/setExtras/consecutive_calls/test.ts @@ -10,5 +10,10 @@ sentryTest('should set extras from multiple consecutive calls', async ({ getLoca const eventData = await getFirstSentryEnvelopeRequest(page, url); expect(eventData.message).toBe('consecutive_calls'); - expect(eventData.extra).toMatchObject({ extra: [], Infinity: 2, null: null, obj: { foo: ['bar', 'baz', 1] } }); + expect(eventData.extra).toMatchObject({ + extra: [], + Infinity: 2, + null: '[Infinity]', + obj: { foo: ['bar', 'baz', 1] }, + }); }); diff --git a/packages/utils/src/normalize.ts b/packages/utils/src/normalize.ts index d86af9561c89..18b41f1c9357 100644 --- a/packages/utils/src/normalize.ts +++ b/packages/utils/src/normalize.ts @@ -81,7 +81,8 @@ function visit( // Get the simple cases out of the way first if ( value == null || // this matches null and undefined -> eqeq not eqeqeq - (['number', 'boolean', 'string'].includes(typeof value) && !Number.isNaN(value)) + ['boolean', 'string'].includes(typeof value) || + (typeof value === 'number' && Number.isFinite(value)) ) { return value as Primitive; } @@ -220,8 +221,8 @@ function stringifyValue( return '[SyntheticEvent]'; } - if (typeof value === 'number' && value !== value) { - return '[NaN]'; + if (typeof value === 'number' && !Number.isFinite(value)) { + return `[${value}]`; } if (typeof value === 'function') { diff --git a/packages/utils/test/normalize.test.ts b/packages/utils/test/normalize.test.ts index 5a2414d52e43..d8a8a1329352 100644 --- a/packages/utils/test/normalize.test.ts +++ b/packages/utils/test/normalize.test.ts @@ -403,6 +403,8 @@ describe('normalize()', () => { describe('changes unserializeable/global values/classes to their respective string representations', () => { test('primitive values', () => { expect(normalize(NaN)).toEqual('[NaN]'); + expect(normalize(Infinity)).toEqual('[Infinity]'); + expect(normalize(-Infinity)).toEqual('[-Infinity]'); expect(normalize(Symbol('dogs'))).toEqual('[Symbol(dogs)]'); expect(normalize(BigInt(1121201212312012))).toEqual('[BigInt: 1121201212312012]'); }); From df79871ab34707932511bd562ef2627e552c7457 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:48:45 +0200 Subject: [PATCH 03/17] feat(nuxt): Adding `experimental_basicServerTracing` option to Nuxt module (#13643) Enabling this option will import the Sentry server config at the top of the server entry file. This can be used when adding the node option `--import` does not work. This however only comes with limited tracing functionality. Example Usage: ```js export default defineNuxtConfig({ sentry: { // ... other options simplifiedDeployment: true }, }) --- packages/nuxt/README.md | 54 ++++++++++++++++------- packages/nuxt/src/common/types.ts | 12 +++++ packages/nuxt/src/module.ts | 21 +++++---- packages/nuxt/src/vite/addServerConfig.ts | 41 +++++++++++++++-- 4 files changed, 102 insertions(+), 26 deletions(-) diff --git a/packages/nuxt/README.md b/packages/nuxt/README.md index df41599e45b9..b16d555a3648 100644 --- a/packages/nuxt/README.md +++ b/packages/nuxt/README.md @@ -37,21 +37,23 @@ functionality related to Nuxt. **What is partly working:** +- Source Maps +- Connected Tracing (Frontend & Backend) - Tracing by setting `tracesSampleRate` - UI (Vue) traces - HTTP (Node) traces -**What is not yet(!) included:** - -- Source Maps -- Nuxt-specific traces and connecting frontend & backend traces - **Known Issues:** -- When adding `sentry.server.config.(ts/js)`, you get this error: "Failed to register ESM hook", but the application - will still work -- When initializing Sentry on the server with `instrument.server.(js|ts)`, you get an `'import-in-the-middle'` error, - and the application won't work +- When adding `sentry.server.config.(ts/js)`, you get an error like this: + "`Failed to register ESM hook (import-in-the-middle/hook.mjs)`". You can add a resolution for `@vercel/nft` to fix + this. This will add the `hook.mjs` file to your build output + ([issue here](https://github.com/unjs/nitro/issues/2703)). + ```json + "resolutions": { + "@vercel/nft": "^0.27.4" + } + ``` ## Automatic Setup @@ -93,16 +95,18 @@ export default defineNuxtConfig({ Add a `sentry.client.config.(js|ts)` file to the root of your project: ```javascript +import { useRuntimeConfig } from '#imports'; import * as Sentry from '@sentry/nuxt'; Sentry.init({ - dsn: process.env.SENTRY_DSN, + // If set up, you can use your runtime config here + dsn: useRuntimeConfig().public.sentry.dsn, }); ``` ### 4. Server-side setup -Add an `instrument.server.mjs` file to your `public` folder: +Add an `sentry.client.config.(js|ts)` file to the root of your project: ```javascript import * as Sentry from '@sentry/nuxt'; @@ -110,18 +114,38 @@ import * as Sentry from '@sentry/nuxt'; // Only run `init` when process.env.SENTRY_DSN is available. if (process.env.SENTRY_DSN) { Sentry.init({ - dsn: process.env.SENTRY_DSN, + dsn: 'your-dsn', }); } ``` -Add an import flag to the `NODE_OPTIONS` of your preview script in the `package.json` file, so the file loads before any -other imports: +The Nuxt runtime config does not work in the Sentry server to technical reasons (it has to be loaded before Nuxt is +loaded). To be able to use `process.env` you either have to add `--env-file=.env` to your node command + +```bash +node --env-file=.env --import ./.output/server/sentry.server.config.mjs .output/server/index.mjs +``` + +or use the `dotenv` package: + +```javascript +import dotenv from 'dotenv'; +import * as Sentry from '@sentry/nuxt'; + +dotenv.config(); + +Sentry.init({ + dsn: process.env.SENTRY_DSN, +}); +``` + +Add an import flag to the Node options of your `node` command (not `nuxt preview`), so the file loads before any other +imports (keep in mind the `.mjs` file ending): ```json { "scripts": { - "preview": "NODE_OPTIONS='--import ./public/instrument.server.mjs' nuxt preview" + "start": "node --import ./.output/server/sentry.server.config.mjs .output/server/index.mjs" } } ``` diff --git a/packages/nuxt/src/common/types.ts b/packages/nuxt/src/common/types.ts index 08dc0d2b805e..5fbe68bd89cb 100644 --- a/packages/nuxt/src/common/types.ts +++ b/packages/nuxt/src/common/types.ts @@ -99,4 +99,16 @@ export type SentryNuxtModuleOptions = { * Enabling this will give you, for example, logs about source maps. */ debug?: boolean; + + /** + * Enabling basic server tracing can be used for environments where modifying the node option `--import` is not possible. + * However, enabling this option only supports limited tracing instrumentation. Only http traces will be collected (but no database-specific traces etc.). + * + * If this option is `true`, the Sentry SDK will import the Sentry server config at the top of the server entry file to load the SDK on the server. + * + * **DO NOT** enable this option if you've already added the node option `--import` in your node start script. This would initialize Sentry twice on the server-side and leads to unexpected issues. + * + * @default false + */ + experimental_basicServerTracing?: boolean; }; diff --git a/packages/nuxt/src/module.ts b/packages/nuxt/src/module.ts index faa48e5c3c26..0afdeaa03de7 100644 --- a/packages/nuxt/src/module.ts +++ b/packages/nuxt/src/module.ts @@ -1,6 +1,6 @@ import { addPlugin, addPluginTemplate, addServerPlugin, createResolver, defineNuxtModule } from '@nuxt/kit'; import type { SentryNuxtModuleOptions } from './common/types'; -import { addServerConfigToBuild } from './vite/addServerConfig'; +import { addSentryTopImport, addServerConfigToBuild } from './vite/addServerConfig'; import { setupSourceMaps } from './vite/sourceMaps'; import { findDefaultSdkInitFile } from './vite/utils'; @@ -62,15 +62,20 @@ export default defineNuxtModule({ if (clientConfigFile || serverConfigFile) { setupSourceMaps(moduleOptions, nuxt); } - if (serverConfigFile && serverConfigFile.includes('.server.config')) { - if (moduleOptions.debug) { - // eslint-disable-next-line no-console - console.log( - `[Sentry] Using your \`${serverConfigFile}\` file for the server-side Sentry configuration. In case you have a \`public/instrument.server\` file, the \`public/instrument.server\` file will be ignored. Make sure the file path in your node \`--import\` option matches the Sentry server config file in your \`.output\` folder and has a \`.mjs\` extension.`, - ); - } + if (serverConfigFile && serverConfigFile.includes('.server.config')) { addServerConfigToBuild(moduleOptions, nuxt, serverConfigFile); + + if (moduleOptions.experimental_basicServerTracing) { + addSentryTopImport(moduleOptions, nuxt); + } else { + if (moduleOptions.debug) { + // eslint-disable-next-line no-console + console.log( + `[Sentry] Using your \`${serverConfigFile}\` file for the server-side Sentry configuration. In case you have a \`public/instrument.server\` file, the \`public/instrument.server\` file will be ignored. Make sure the file path in your node \`--import\` option matches the Sentry server config file in your \`.output\` folder and has a \`.mjs\` extension.`, + ); + } + } } }, }); diff --git a/packages/nuxt/src/vite/addServerConfig.ts b/packages/nuxt/src/vite/addServerConfig.ts index dc1fc21dd6bd..dee15ee34dce 100644 --- a/packages/nuxt/src/vite/addServerConfig.ts +++ b/packages/nuxt/src/vite/addServerConfig.ts @@ -1,5 +1,4 @@ import * as fs from 'fs'; -import * as path from 'path'; import { createResolver } from '@nuxt/kit'; import type { Nuxt } from '@nuxt/schema'; import type { SentryNuxtModuleOptions } from '../common/types'; @@ -30,8 +29,9 @@ export function addServerConfigToBuild( * This is necessary because we need to reference this file path in the node --import option. */ nuxt.hook('close', async () => { - const source = path.resolve('.nuxt/dist/server/sentry.server.config.mjs'); - const destination = path.resolve('.output/server/sentry.server.config.mjs'); + const rootDirResolver = createResolver(nuxt.options.rootDir); + const source = rootDirResolver.resolve('.nuxt/dist/server/sentry.server.config.mjs'); + const destination = rootDirResolver.resolve('.output/server/sentry.server.config.mjs'); try { await fs.promises.access(source, fs.constants.F_OK); @@ -55,3 +55,38 @@ export function addServerConfigToBuild( }); }); } + +/** + * Adds the Sentry server config import at the top of the server entry file to load the SDK on the server. + * This is necessary for environments where modifying the node option `--import` is not possible. + * However, only limited tracing instrumentation is supported when doing this. + */ +export function addSentryTopImport(moduleOptions: SentryNuxtModuleOptions, nuxt: Nuxt): void { + nuxt.hook('close', async () => { + const rootDirResolver = createResolver(nuxt.options.rootDir); + const entryFilePath = rootDirResolver.resolve('.output/server/index.mjs'); + + try { + fs.readFile(entryFilePath, 'utf8', (err, data) => { + const updatedContent = `import './sentry.server.config.mjs';\n${data}`; + + fs.writeFile(entryFilePath, updatedContent, 'utf8', () => { + if (moduleOptions.debug) { + // eslint-disable-next-line no-console + console.log( + `[Sentry] Successfully added the Sentry import to the server entry file "\`${entryFilePath}\`"`, + ); + } + }); + }); + } catch (err) { + if (moduleOptions.debug) { + // eslint-disable-next-line no-console + console.warn( + `[Sentry] An error occurred when trying to add the Sentry import to the server entry file "\`${entryFilePath}\`":`, + err, + ); + } + } + }); +} From bcd2a175e669d1435ef38c449805921b5d7c55d0 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Fri, 13 Sep 2024 10:42:18 +0200 Subject: [PATCH 04/17] fix(node): Don't overwrite local variables for re-thrown errors (#13644) Another way to fix this issue would be to check via the debugger if the property exists already on the error object and bail early before we have local variables. However, this would add an extra round trip call via the debugger for every error. Since re-thrown errors are far less likely, I decided instead to just not overwrite existing local variables. This PR also adds a `Debugger.resume` call in the catch case to ensure that they debugger will always resume even if we get errors while debugging. It's worth noting that this only fixes the issue in Node v20+ where we use the async debugging interface. --- .../LocalVariables/local-variables-rethrow.js | 45 +++++++++++++++++++ .../suites/public-api/LocalVariables/test.ts | 8 ++++ .../integrations/local-variables/worker.ts | 10 +++-- 3 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 dev-packages/node-integration-tests/suites/public-api/LocalVariables/local-variables-rethrow.js diff --git a/dev-packages/node-integration-tests/suites/public-api/LocalVariables/local-variables-rethrow.js b/dev-packages/node-integration-tests/suites/public-api/LocalVariables/local-variables-rethrow.js new file mode 100644 index 000000000000..744cb747c70c --- /dev/null +++ b/dev-packages/node-integration-tests/suites/public-api/LocalVariables/local-variables-rethrow.js @@ -0,0 +1,45 @@ +/* eslint-disable no-unused-vars */ +const Sentry = require('@sentry/node'); +const { loggingTransport } = require('@sentry-internal/node-integration-tests'); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + includeLocalVariables: true, + transport: loggingTransport, +}); + +class Some { + two(name) { + throw new Error('Enough!'); + } +} + +function one(name) { + const arr = [1, '2', null]; + const obj = { + name, + num: 5, + }; + const bool = false; + const num = 0; + const str = ''; + const something = undefined; + const somethingElse = null; + + const ty = new Some(); + + ty.two(name); +} + +setTimeout(() => { + try { + try { + one('some name'); + } catch (e) { + const more = 'here'; + throw e; + } + } catch (e) { + Sentry.captureException(e); + } +}, 1000); diff --git a/dev-packages/node-integration-tests/suites/public-api/LocalVariables/test.ts b/dev-packages/node-integration-tests/suites/public-api/LocalVariables/test.ts index 2640ecf94461..bf01ed999708 100644 --- a/dev-packages/node-integration-tests/suites/public-api/LocalVariables/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/LocalVariables/test.ts @@ -78,6 +78,14 @@ conditionalTest({ min: 18 })('LocalVariables integration', () => { }); }); + conditionalTest({ min: 20 })('Node v20+', () => { + test('Should retain original local variables when error is re-thrown', done => { + createRunner(__dirname, 'local-variables-rethrow.js') + .expect({ event: EXPECTED_LOCAL_VARIABLES_EVENT }) + .start(done); + }); + }); + test('Includes local variables for caught exceptions when enabled', done => { createRunner(__dirname, 'local-variables-caught.js').expect({ event: EXPECTED_LOCAL_VARIABLES_EVENT }).start(done); }); diff --git a/packages/node/src/integrations/local-variables/worker.ts b/packages/node/src/integrations/local-variables/worker.ts index eb4fee87947c..f0b3e20ce9b2 100644 --- a/packages/node/src/integrations/local-variables/worker.ts +++ b/packages/node/src/integrations/local-variables/worker.ts @@ -118,7 +118,9 @@ async function handlePaused( // We write the local variables to a property on the error object. These can be read by the integration as the error // event pass through the SDK event pipeline await session.post('Runtime.callFunctionOn', { - functionDeclaration: `function() { this.${LOCAL_VARIABLES_KEY} = ${JSON.stringify(frames)}; }`, + functionDeclaration: `function() { this.${LOCAL_VARIABLES_KEY} = this.${LOCAL_VARIABLES_KEY} || ${JSON.stringify( + frames, + )}; }`, silent: true, objectId, }); @@ -156,8 +158,10 @@ async function startDebugger(): Promise { }, 1_000); } }, - _ => { - // ignore any errors + async _ => { + if (isPaused) { + await session.post('Debugger.resume'); + } }, ); }); From 8711bdda72a3dbe401f8ec714625653ef8e1ca4e Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 13 Sep 2024 10:57:45 +0200 Subject: [PATCH 05/17] deps(sveltekit): Bump `sorcery` to 1.0.0 (#13660) Bump `sorcery` to version 1.0.0. the main changes made were to move the package to output ESM code. The SvelteKit build complained a bit about ESM initially but moving from a static to a dynamic import worked, so I'll take that. --- packages/sveltekit/package.json | 2 +- packages/sveltekit/src/vite/sourceMaps.ts | 5 ++- .../sveltekit/test/vite/sourceMaps.test.ts | 15 ++++++-- yarn.lock | 38 ++++++------------- 4 files changed, 27 insertions(+), 33 deletions(-) diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index 32446d0b2246..2876aa4aa1ab 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -49,7 +49,7 @@ "@sentry/vite-plugin": "2.22.3", "magic-string": "0.30.7", "magicast": "0.2.8", - "sorcery": "0.11.0" + "sorcery": "1.0.0" }, "devDependencies": { "@babel/types": "7.20.7", diff --git a/packages/sveltekit/src/vite/sourceMaps.ts b/packages/sveltekit/src/vite/sourceMaps.ts index b2ceace40529..d228eb6da30e 100644 --- a/packages/sveltekit/src/vite/sourceMaps.ts +++ b/packages/sveltekit/src/vite/sourceMaps.ts @@ -5,8 +5,6 @@ import { getSentryRelease } from '@sentry/node'; import { escapeStringForRegex, uuid4 } from '@sentry/utils'; import type { SentryVitePluginOptions } from '@sentry/vite-plugin'; import { sentryVitePlugin } from '@sentry/vite-plugin'; -// @ts-expect-error -sorcery has no types :( -import * as sorcery from 'sorcery'; import type { Plugin } from 'vite'; import MagicString from 'magic-string'; @@ -187,6 +185,9 @@ export async function makeCustomSentryVitePlugins(options?: CustomSentryVitePlug // eslint-disable-next-line no-console debug && console.log('[Source Maps Plugin] Flattening source maps'); + // @ts-expect-error - we're using dynamic import here and TS complains about that. It works though. + const sorcery = await import('sorcery'); + for (const file of jsFiles) { try { await (sorcery as Sorcery).load(file).then(async chain => { diff --git a/packages/sveltekit/test/vite/sourceMaps.test.ts b/packages/sveltekit/test/vite/sourceMaps.test.ts index c25ec48a53cb..2d12c9835b58 100644 --- a/packages/sveltekit/test/vite/sourceMaps.test.ts +++ b/packages/sveltekit/test/vite/sourceMaps.test.ts @@ -17,6 +17,15 @@ vi.mock('@sentry/vite-plugin', async () => { }; }); +vi.mock('sorcery', async () => { + return { + load: vi.fn().mockResolvedValue({ + apply: vi.fn().mockResolvedValue(undefined), + write: vi.fn().mockResolvedValue(undefined), + }), + }; +}); + beforeEach(() => { vi.clearAllMocks(); }); @@ -79,7 +88,7 @@ describe('makeCustomSentryVitePlugin()', () => { // @ts-expect-error this function exists! plugin.configResolved({ build: { ssr: true } }); // @ts-expect-error this function exists! - plugin.closeBundle(); + await plugin.closeBundle(); expect(mockedSentryVitePlugin.writeBundle).toHaveBeenCalledTimes(1); }); @@ -89,7 +98,7 @@ describe('makeCustomSentryVitePlugin()', () => { // @ts-expect-error this function exists! plugin.configResolved({ build: { ssr: false } }); // @ts-expect-error this function exists! - plugin.closeBundle(); + await plugin.closeBundle(); expect(mockedSentryVitePlugin.writeBundle).not.toHaveBeenCalled(); }); }); @@ -110,7 +119,7 @@ describe('makeCustomSentryVitePlugin()', () => { // @ts-expect-error this function exists! plugin.configResolved({ build: { ssr: true } }); // @ts-expect-error this function exists! - plugin.closeBundle(); + await plugin.closeBundle(); expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to upload source maps')); expect(consoleLogSpy).toHaveBeenCalled(); diff --git a/yarn.lock b/yarn.lock index a9e1df0710b6..9f6846f29f95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13474,16 +13474,16 @@ bson@^1.1.4: resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.6.tgz#fb819be9a60cd677e0853aee4ca712a785d6618a" integrity sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg== -buffer-crc32@^0.2.5, buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - buffer-crc32@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz#a10993b9055081d55304bd9feb4a072de179f405" integrity sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w== +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" @@ -17028,11 +17028,6 @@ es6-object-assign@^1.1.0: resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= -es6-promise@^3.1.2: - version "3.3.1" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" - integrity sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg== - esbuild-android-64@0.15.18: version "0.15.18" resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz#20a7ae1416c8eaade917fb2453c1259302c637a5" @@ -29334,7 +29329,7 @@ rfdc@^1.4.1: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== -rimraf@^2.2.8, rimraf@^2.3.4, rimraf@^2.4.3, rimraf@^2.5.2, rimraf@^2.5.3, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: +rimraf@^2.2.8, rimraf@^2.3.4, rimraf@^2.4.3, rimraf@^2.5.3, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -29662,16 +29657,6 @@ safe-stable-stringify@^2.4.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sander@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/sander/-/sander-0.5.1.tgz#741e245e231f07cafb6fdf0f133adfa216a502ad" - integrity sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA== - dependencies: - es6-promise "^3.1.2" - graceful-fs "^4.1.3" - mkdirp "^0.5.1" - rimraf "^2.5.2" - sane@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" @@ -30446,15 +30431,14 @@ solid-use@^0.8.0: resolved "https://registry.yarnpkg.com/solid-use/-/solid-use-0.8.0.tgz#d46258c45edb0f4c621285e0ad1aa6b6a674d79b" integrity sha512-YX+XmcKLvSx3bwMimMhFy40ZkDnShnUcEw6cW6fSscwKEgl1TG3GlgAvkBmQ3AeWjvQSd8+HGTr82ImsrjkkqA== -sorcery@0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/sorcery/-/sorcery-0.11.0.tgz#310c80ee993433854bb55bb9aa4003acd147fca8" - integrity sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw== +sorcery@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/sorcery/-/sorcery-1.0.0.tgz#b5bb81fb9706c0c240f5f2d3214b4d2be649e07f" + integrity sha512-5ay9oJE+7sNmhzl3YNG18jEEEf4AOQCM/FAqR5wMmzqd1FtRorFbJXn3w3SKOhbiQaVgHM+Q1lszZspjri7bpA== dependencies: "@jridgewell/sourcemap-codec" "^1.4.14" - buffer-crc32 "^0.2.5" minimist "^1.2.0" - sander "^0.5.0" + tiny-glob "^0.2.9" sort-keys@^2.0.0: version "2.0.0" From e8a016e041cd8859ea0c58d8ccecd348203a1452 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:17:28 +0000 Subject: [PATCH 06/17] chore(deps): bump express from 4.19.2 to 4.20.0 in /dev-packages/e2e-tests/test-applications/node-express (#13685) --- .../e2e-tests/test-applications/node-express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/node-express/package.json b/dev-packages/e2e-tests/test-applications/node-express/package.json index 34643b63553e..97947e70d06e 100644 --- a/dev-packages/e2e-tests/test-applications/node-express/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express/package.json @@ -18,7 +18,7 @@ "@trpc/client": "10.45.2", "@types/express": "4.17.17", "@types/node": "18.15.1", - "express": "4.19.2", + "express": "4.20.0", "typescript": "4.9.5", "zod": "~3.22.4" }, From f44785cac6b393e5aba1ecb51f5c2a0ef5a91328 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Fri, 13 Sep 2024 11:21:12 +0200 Subject: [PATCH 07/17] build: Add codecov test analytics for playwright tests (#13654) https://docs.codecov.com/docs/test-result-ingestion-beta https://playwright.dev/docs/test-reporters Adds codecov test analytics to the repo, specifically for our playwright tests. This works by using the junit reporter with playwright, and then uploading that via the `codecov/test-results-action@v1` GitHub Action. --- .github/workflows/build.yml | 22 +++++++++++++++++++ .../browser-integration-tests/package.json | 2 +- .../playwright.config.ts | 2 ++ .../ember-classic/playwright.config.ts | 2 +- .../ember-embroider/playwright.config.ts | 2 +- .../playwright.config.mjs | 2 +- .../playwright.config.mjs | 2 +- .../test-utils/src/playwright-config.ts | 2 +- packages/remix/package.json | 2 +- packages/remix/playwright.config.ts | 1 + 10 files changed, 32 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 38102ff204c7..c1ff3d4b8e9c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -610,6 +610,13 @@ jobs: overwrite: true retention-days: 7 + - name: Upload test results to Codecov + if: cancelled() == false + uses: codecov/test-results-action@v1 + with: + directory: dev-packages/browser-integration-tests + token: ${{ secrets.CODECOV_TOKEN }} + job_browser_loader_tests: name: PW ${{ matrix.bundle }} Tests needs: [job_get_metadata, job_build] @@ -653,6 +660,7 @@ jobs: run: | cd dev-packages/browser-integration-tests yarn test:loader + - name: Upload Playwright Traces uses: actions/upload-artifact@v4 if: failure() @@ -662,6 +670,13 @@ jobs: overwrite: true retention-days: 7 + - name: Upload test results to Codecov + if: cancelled() == false + uses: codecov/test-results-action@v1 + with: + directory: dev-packages/browser-integration-tests + token: ${{ secrets.CODECOV_TOKEN }} + job_check_for_faulty_dts: name: Check for faulty .d.ts files needs: [job_get_metadata, job_build] @@ -1013,6 +1028,13 @@ jobs: overwrite: true retention-days: 7 + - name: Upload test results to Codecov + if: cancelled() == false + uses: codecov/test-results-action@v1 + with: + directory: dev-packages/e2e-tests + token: ${{ secrets.CODECOV_TOKEN }} + job_optional_e2e_tests: name: E2E ${{ matrix.label || matrix.test-application }} Test # We only run E2E tests for non-fork PRs because the E2E tests require secrets to work and they can't be accessed from forks diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index 719dba1eece3..63edc3c9318f 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -35,7 +35,7 @@ "test:loader:replay_buffer": "PW_BUNDLE=loader_replay_buffer yarn test:loader", "test:loader:full": "PW_BUNDLE=loader_tracing_replay yarn test:loader", "test:loader:debug": "PW_BUNDLE=loader_debug yarn test:loader", - "test:ci": "yarn test:all --reporter='line'", + "test:ci": "yarn test:all", "test:update-snapshots": "yarn test:all --update-snapshots", "test:detect-flaky": "ts-node scripts/detectFlakyTests.ts" }, diff --git a/dev-packages/browser-integration-tests/playwright.config.ts b/dev-packages/browser-integration-tests/playwright.config.ts index 45a548311eef..498e7529f37a 100644 --- a/dev-packages/browser-integration-tests/playwright.config.ts +++ b/dev-packages/browser-integration-tests/playwright.config.ts @@ -30,6 +30,8 @@ const config: PlaywrightTestConfig = { }, ], + reporter: process.env.CI ? [['line'], ['junit', { outputFile: 'results.junit.xml' }]] : 'list', + globalSetup: require.resolve('./playwright.setup.ts'), globalTeardown: require.resolve('./playwright.teardown.ts'), }; diff --git a/dev-packages/e2e-tests/test-applications/ember-classic/playwright.config.ts b/dev-packages/e2e-tests/test-applications/ember-classic/playwright.config.ts index 6c2442587de4..a092503a9fc2 100644 --- a/dev-packages/e2e-tests/test-applications/ember-classic/playwright.config.ts +++ b/dev-packages/e2e-tests/test-applications/ember-classic/playwright.config.ts @@ -35,7 +35,7 @@ const config: PlaywrightTestConfig = { forbidOnly: !!process.env.CI, retries: 0, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'list', + reporter: process.env.CI ? [['line'], ['junit', { outputFile: 'results.junit.xml' }]] : 'list', /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ diff --git a/dev-packages/e2e-tests/test-applications/ember-embroider/playwright.config.ts b/dev-packages/e2e-tests/test-applications/ember-embroider/playwright.config.ts index 6c2442587de4..a092503a9fc2 100644 --- a/dev-packages/e2e-tests/test-applications/ember-embroider/playwright.config.ts +++ b/dev-packages/e2e-tests/test-applications/ember-embroider/playwright.config.ts @@ -35,7 +35,7 @@ const config: PlaywrightTestConfig = { forbidOnly: !!process.env.CI, retries: 0, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'list', + reporter: process.env.CI ? [['line'], ['junit', { outputFile: 'results.junit.xml' }]] : 'list', /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ diff --git a/dev-packages/e2e-tests/test-applications/node-express-send-to-sentry/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/node-express-send-to-sentry/playwright.config.mjs index 9340e7e9436a..f29509db795c 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-send-to-sentry/playwright.config.mjs +++ b/dev-packages/e2e-tests/test-applications/node-express-send-to-sentry/playwright.config.mjs @@ -23,7 +23,7 @@ const config = { /* Retry on CI only */ retries: 0, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'list', + reporter: process.env.CI ? [['line'], ['junit', { outputFile: 'results.junit.xml' }]] : 'list', /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/playwright.config.mjs index 7d04e3b6dd4b..aa8fc9bfd4b7 100644 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/playwright.config.mjs +++ b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/playwright.config.mjs @@ -23,7 +23,7 @@ const config = { /* Opt out of parallel tests on CI. */ workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'list', + reporter: process.env.CI ? [['line'], ['junit', { outputFile: 'results.junit.xml' }]] : 'list', /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ diff --git a/dev-packages/test-utils/src/playwright-config.ts b/dev-packages/test-utils/src/playwright-config.ts index d30c8cad4475..c380c9547ec0 100644 --- a/dev-packages/test-utils/src/playwright-config.ts +++ b/dev-packages/test-utils/src/playwright-config.ts @@ -37,7 +37,7 @@ export function getPlaywrightConfig( /* In dev mode some apps are flaky, so we allow retry there... */ retries: testEnv === 'development' ? 3 : 0, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: process.env.CI ? 'line' : 'list', + reporter: process.env.CI ? [['line'], ['junit', { outputFile: 'results.junit.xml' }]] : 'list', /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ diff --git a/packages/remix/package.json b/packages/remix/package.json index ada7991ead3c..c338d0df30a8 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -106,7 +106,7 @@ "test:integration:prepare": "(cd test/integration && yarn install)", "test:integration:clean": "(cd test/integration && rimraf .cache node_modules build)", "test:integration:client": "yarn playwright install-deps && yarn playwright test test/integration/test/client/ --project='chromium'", - "test:integration:client:ci": "yarn test:integration:client --reporter='line'", + "test:integration:client:ci": "yarn test:integration:client", "test:integration:server": "export NODE_OPTIONS='--stack-trace-limit=25' && vitest run", "test:unit": "jest", "test:watch": "jest --watch", diff --git a/packages/remix/playwright.config.ts b/packages/remix/playwright.config.ts index 72e9bd749a52..a1570f27f50d 100644 --- a/packages/remix/playwright.config.ts +++ b/packages/remix/playwright.config.ts @@ -8,6 +8,7 @@ const config: PlaywrightTestConfig = { }, // Run tests inside of a single file in parallel fullyParallel: true, + reporter: process.env.CI ? [['line'], ['junit', { outputFile: 'results.junit.xml' }]] : 'list', // Use 3 workers on CI, else use defaults (based on available CPU cores) // Note that 3 is a random number selected to work well with our CI setup workers: process.env.CI ? 3 : undefined, From 8dd3de137b5ffc71b75af894647a8fcd15036c3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 09:36:45 +0000 Subject: [PATCH 08/17] chore(deps): bump express from 4.19.2 to 4.20.0 in /dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation (#13687) --- .../node-express-incorrect-instrumentation/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation/package.json b/dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation/package.json index 3df947bb58c5..a8b325bc075d 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-incorrect-instrumentation/package.json @@ -18,7 +18,7 @@ "@trpc/client": "10.45.2", "@types/express": "4.17.17", "@types/node": "18.15.1", - "express": "4.19.2", + "express": "4.20.0", "typescript": "4.9.5", "zod": "~3.22.4" }, From 0f122a153ee2ece925f41c850c233222748b1704 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Fri, 13 Sep 2024 11:56:55 +0200 Subject: [PATCH 09/17] fix(nestjs): Preserve original function name on `SentryTraced` functions (#13684) --- .../test-applications/nestjs-basic/src/app.controller.ts | 5 +++++ .../test-applications/nestjs-basic/src/app.service.ts | 5 +++++ .../nestjs-basic/tests/span-decorator.test.ts | 7 +++++++ packages/nestjs/src/decorators/sentry-traced.ts | 9 +++++++++ 4 files changed, 26 insertions(+) diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts index 75308e8f0ea9..77e25a72dad5 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts @@ -116,4 +116,9 @@ export class AppController { testServiceWithCanActivate() { return this.appService.canActivate(); } + + @Get('test-function-name') + testFunctionName() { + return this.appService.getFunctionName(); + } } diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.service.ts index 3e4639040a7e..72aef6947a6c 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.service.ts @@ -58,6 +58,11 @@ export class AppService { return { result: 'test' }; } + @SentryTraced('return the function name') + getFunctionName(): { result: string } { + return { result: this.getFunctionName.name }; + } + async testSpanDecoratorSync() { const returned = this.getString(); // Will fail if getString() is async, because returned will be a Promise<> diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/span-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/span-decorator.test.ts index 4b3ea2c0ba40..ee7666a50f18 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/span-decorator.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/span-decorator.test.ts @@ -70,3 +70,10 @@ test('Transaction includes span and correct value for decorated sync function', ]), ); }); + +test('preserves original function name on decorated functions', async ({ baseURL }) => { + const response = await fetch(`${baseURL}/test-function-name`); + const body = await response.json(); + + expect(body.result).toEqual('getFunctionName'); +}); diff --git a/packages/nestjs/src/decorators/sentry-traced.ts b/packages/nestjs/src/decorators/sentry-traced.ts index b9ef861bc3b2..2f90e4dab5d9 100644 --- a/packages/nestjs/src/decorators/sentry-traced.ts +++ b/packages/nestjs/src/decorators/sentry-traced.ts @@ -20,6 +20,15 @@ export function SentryTraced(op: string = 'function') { }, ); }; + + // preserve the original name on the decorated function + Object.defineProperty(descriptor.value, 'name', { + value: originalMethod.name, + configurable: true, + enumerable: true, + writable: true, + }); + return descriptor; }; } From cbb94758796b875c68a1a309fe2f1faf15c1a1a4 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 13 Sep 2024 12:52:43 +0200 Subject: [PATCH 10/17] feat(wasm): Unconditionally parse instruction addresses (#13655) Currently we are only setting the `platform` and `instruction_addr` for a wasm frame if we find a matching debug image. This is not always the case and unconditionally parsing the instruction address is almost always desirable. --- packages/wasm/src/index.ts | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/wasm/src/index.ts b/packages/wasm/src/index.ts index 88eb1915ce06..832a7e89b687 100644 --- a/packages/wasm/src/index.ts +++ b/packages/wasm/src/index.ts @@ -13,17 +13,18 @@ const _wasmIntegration = (() => { patchWebAssembly(); }, processEvent(event: Event): Event { - let haveWasm = false; + let hasAtLeastOneWasmFrameWithImage = false; if (event.exception && event.exception.values) { event.exception.values.forEach(exception => { if (exception.stacktrace && exception.stacktrace.frames) { - haveWasm = haveWasm || patchFrames(exception.stacktrace.frames); + hasAtLeastOneWasmFrameWithImage = + hasAtLeastOneWasmFrameWithImage || patchFrames(exception.stacktrace.frames); } }); } - if (haveWasm) { + if (hasAtLeastOneWasmFrameWithImage) { event.debug_meta = event.debug_meta || {}; event.debug_meta.images = [...(event.debug_meta.images || []), ...getImages()]; } @@ -37,10 +38,11 @@ export const wasmIntegration = defineIntegration(_wasmIntegration); /** * Patches a list of stackframes with wasm data needed for server-side symbolication - * if applicable. Returns true if any frames were patched. + * if applicable. Returns true if the provided list of stack frames had at least one + * matching registered image. */ function patchFrames(frames: Array): boolean { - let haveWasm = false; + let hasAtLeastOneWasmFrameWithImage = false; frames.forEach(frame => { if (!frame.filename) { return; @@ -50,14 +52,15 @@ function patchFrames(frames: Array): boolean { | [string, string, string]; if (match) { const index = getImage(match[1]); + frame.instruction_addr = match[2]; + frame.filename = match[1]; + frame.platform = 'native'; + if (index >= 0) { - frame.instruction_addr = match[2]; frame.addr_mode = `rel:${index}`; - frame.filename = match[1]; - frame.platform = 'native'; - haveWasm = true; + hasAtLeastOneWasmFrameWithImage = true; } } }); - return haveWasm; + return hasAtLeastOneWasmFrameWithImage; } From 7878d197e340e1fe4621a0b0a2f9b77d2867726f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:11:52 +0000 Subject: [PATCH 11/17] chore(deps): bump express from 4.19.2 to 4.20.0 in /dev-packages/e2e-tests/test-applications/node-express-esm-without-loader (#13689) --- .../node-express-esm-without-loader/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/node-express-esm-without-loader/package.json b/dev-packages/e2e-tests/test-applications/node-express-esm-without-loader/package.json index 7939cf85a7ca..844ca51fd038 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-esm-without-loader/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-esm-without-loader/package.json @@ -11,7 +11,7 @@ "dependencies": { "@sentry/node": "latest || *", "@sentry/opentelemetry": "latest || *", - "express": "4.19.2" + "express": "4.20.0" }, "devDependencies": { "@playwright/test": "^1.44.1", From 17951da4166c7d75573f066407ed08b83077df37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:22:05 +0000 Subject: [PATCH 12/17] chore(deps): bump express from 4.19.2 to 4.20.0 in /dev-packages/e2e-tests/test-applications/node-express-esm-preload (#13691) --- .../test-applications/node-express-esm-preload/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/node-express-esm-preload/package.json b/dev-packages/e2e-tests/test-applications/node-express-esm-preload/package.json index df6fcaf29adc..03f483307290 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-esm-preload/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-esm-preload/package.json @@ -11,7 +11,7 @@ "dependencies": { "@sentry/node": "latest || *", "@sentry/opentelemetry": "latest || *", - "express": "4.19.2" + "express": "4.20.0" }, "devDependencies": { "@playwright/test": "^1.44.1", From 79efa636f053b82cce6748654e51996fa7e7784f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:32:14 +0000 Subject: [PATCH 13/17] chore(deps): bump express from 4.19.2 to 4.20.0 in /dev-packages/e2e-tests/test-applications/node-express-esm-loader (#13692) --- .../test-applications/node-express-esm-loader/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/node-express-esm-loader/package.json b/dev-packages/e2e-tests/test-applications/node-express-esm-loader/package.json index 61fc40619560..6156211e27f8 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-esm-loader/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-esm-loader/package.json @@ -11,7 +11,7 @@ "dependencies": { "@sentry/node": "latest || *", "@sentry/opentelemetry": "latest || *", - "express": "4.19.2" + "express": "4.20.0" }, "devDependencies": { "@playwright/test": "^1.44.1", From 69e74271e3bce4d76460deef9352756ebe57b930 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:42:21 +0000 Subject: [PATCH 14/17] chore(deps): bump express from 4.19.2 to 4.20.0 in /dev-packages/e2e-tests/test-applications/node-express-cjs-preload (#13694) --- .../test-applications/node-express-cjs-preload/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/package.json b/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/package.json index 5a3074df94eb..363c1e06636c 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/package.json +++ b/dev-packages/e2e-tests/test-applications/node-express-cjs-preload/package.json @@ -11,7 +11,7 @@ "dependencies": { "@sentry/node": "latest || *", "@sentry/opentelemetry": "latest || *", - "express": "4.19.2" + "express": "4.20.0" }, "devDependencies": { "@playwright/test": "^1.44.1", From 5d0094a1d015566b11c408331418ae84c9b3a361 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 13 Sep 2024 14:06:34 +0200 Subject: [PATCH 15/17] fix: Ensure all logs are wrapped with `consoleSandbox` (#13690) To avoid infinite loops etc. --- packages/angular/src/errorhandler.ts | 4 +- .../integrations/local-variables/worker.ts | 3 +- packages/nuxt/src/module.ts | 11 +++-- packages/nuxt/src/vite/addServerConfig.ts | 45 +++++++++++-------- packages/sveltekit/src/server/handleError.ts | 3 +- 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/packages/angular/src/errorhandler.ts b/packages/angular/src/errorhandler.ts index 14ca380ea3ea..f1771ba81b7e 100644 --- a/packages/angular/src/errorhandler.ts +++ b/packages/angular/src/errorhandler.ts @@ -4,7 +4,7 @@ import { Inject, Injectable } from '@angular/core'; import * as Sentry from '@sentry/browser'; import type { ReportDialogOptions } from '@sentry/browser'; import type { Event } from '@sentry/types'; -import { isString } from '@sentry/utils'; +import { consoleSandbox, isString } from '@sentry/utils'; import { runOutsideAngular } from './zone'; @@ -119,7 +119,7 @@ class SentryErrorHandler implements AngularErrorHandler, OnDestroy { // When in development mode, log the error to console for immediate feedback. if (this._options.logErrors) { // eslint-disable-next-line no-console - console.error(extractedError); + consoleSandbox(() => console.error(extractedError)); } // Optionally show user dialog to provide details on what happened. diff --git a/packages/node/src/integrations/local-variables/worker.ts b/packages/node/src/integrations/local-variables/worker.ts index f0b3e20ce9b2..77299c0aff29 100644 --- a/packages/node/src/integrations/local-variables/worker.ts +++ b/packages/node/src/integrations/local-variables/worker.ts @@ -1,6 +1,7 @@ import type { Debugger, InspectorNotification, Runtime } from 'node:inspector'; import { Session } from 'node:inspector/promises'; import { workerData } from 'node:worker_threads'; +import { consoleSandbox } from '@sentry/utils'; import type { LocalVariablesWorkerArgs, PausedExceptionEvent, RateLimitIncrement, Variables } from './common'; import { LOCAL_VARIABLES_KEY } from './common'; import { createRateLimiter } from './common'; @@ -10,7 +11,7 @@ const options: LocalVariablesWorkerArgs = workerData; function log(...args: unknown[]): void { if (options.debug) { // eslint-disable-next-line no-console - console.log('[LocalVariables Worker]', ...args); + consoleSandbox(() => console.log('[LocalVariables Worker]', ...args)); } } diff --git a/packages/nuxt/src/module.ts b/packages/nuxt/src/module.ts index 0afdeaa03de7..f43f30d7e5ee 100644 --- a/packages/nuxt/src/module.ts +++ b/packages/nuxt/src/module.ts @@ -1,4 +1,5 @@ import { addPlugin, addPluginTemplate, addServerPlugin, createResolver, defineNuxtModule } from '@nuxt/kit'; +import { consoleSandbox } from '@sentry/utils'; import type { SentryNuxtModuleOptions } from './common/types'; import { addSentryTopImport, addServerConfigToBuild } from './vite/addServerConfig'; import { setupSourceMaps } from './vite/sourceMaps'; @@ -70,10 +71,12 @@ export default defineNuxtModule({ addSentryTopImport(moduleOptions, nuxt); } else { if (moduleOptions.debug) { - // eslint-disable-next-line no-console - console.log( - `[Sentry] Using your \`${serverConfigFile}\` file for the server-side Sentry configuration. In case you have a \`public/instrument.server\` file, the \`public/instrument.server\` file will be ignored. Make sure the file path in your node \`--import\` option matches the Sentry server config file in your \`.output\` folder and has a \`.mjs\` extension.`, - ); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.log( + `[Sentry] Using your \`${serverConfigFile}\` file for the server-side Sentry configuration. In case you have a \`public/instrument.server\` file, the \`public/instrument.server\` file will be ignored. Make sure the file path in your node \`--import\` option matches the Sentry server config file in your \`.output\` folder and has a \`.mjs\` extension.`, + ); + }); } } } diff --git a/packages/nuxt/src/vite/addServerConfig.ts b/packages/nuxt/src/vite/addServerConfig.ts index dee15ee34dce..60f2452cde5b 100644 --- a/packages/nuxt/src/vite/addServerConfig.ts +++ b/packages/nuxt/src/vite/addServerConfig.ts @@ -1,6 +1,7 @@ import * as fs from 'fs'; import { createResolver } from '@nuxt/kit'; import type { Nuxt } from '@nuxt/schema'; +import { consoleSandbox } from '@sentry/utils'; import type { SentryNuxtModuleOptions } from '../common/types'; /** @@ -38,18 +39,22 @@ export function addServerConfigToBuild( await fs.promises.copyFile(source, destination); if (moduleOptions.debug) { - // eslint-disable-next-line no-console - console.log( - `[Sentry] Successfully added the content of the \`${serverConfigFile}\` file to \`${destination}\``, - ); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.log( + `[Sentry] Successfully added the content of the \`${serverConfigFile}\` file to \`${destination}\``, + ); + }); } } catch (error) { if (moduleOptions.debug) { - // eslint-disable-next-line no-console - console.warn( - `[Sentry] An error occurred when trying to add the \`${serverConfigFile}\` file to the \`.output\` directory`, - error, - ); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + `[Sentry] An error occurred when trying to add the \`${serverConfigFile}\` file to the \`.output\` directory`, + error, + ); + }); } } }); @@ -72,20 +77,24 @@ export function addSentryTopImport(moduleOptions: SentryNuxtModuleOptions, nuxt: fs.writeFile(entryFilePath, updatedContent, 'utf8', () => { if (moduleOptions.debug) { - // eslint-disable-next-line no-console - console.log( - `[Sentry] Successfully added the Sentry import to the server entry file "\`${entryFilePath}\`"`, - ); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.log( + `[Sentry] Successfully added the Sentry import to the server entry file "\`${entryFilePath}\`"`, + ); + }); } }); }); } catch (err) { if (moduleOptions.debug) { - // eslint-disable-next-line no-console - console.warn( - `[Sentry] An error occurred when trying to add the Sentry import to the server entry file "\`${entryFilePath}\`":`, - err, - ); + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + `[Sentry] An error occurred when trying to add the Sentry import to the server entry file "\`${entryFilePath}\`":`, + err, + ); + }); } } }); diff --git a/packages/sveltekit/src/server/handleError.ts b/packages/sveltekit/src/server/handleError.ts index 1289e76a5ee2..f61251245c4d 100644 --- a/packages/sveltekit/src/server/handleError.ts +++ b/packages/sveltekit/src/server/handleError.ts @@ -1,4 +1,5 @@ import { captureException } from '@sentry/node'; +import { consoleSandbox } from '@sentry/utils'; import type { HandleServerError } from '@sveltejs/kit'; import { flushIfServerless } from './utils'; @@ -8,7 +9,7 @@ import { flushIfServerless } from './utils'; function defaultErrorHandler({ error }: Parameters[0]): ReturnType { // @ts-expect-error this conforms to the default implementation (including this ts-expect-error) // eslint-disable-next-line no-console - console.error(error && error.stack); + consoleSandbox(() => console.error(error && error.stack)); } type HandleServerErrorInput = Parameters[0]; From 214008317da9a088321fdf5ba7b4a82f7a653f75 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 13 Sep 2024 14:11:47 +0200 Subject: [PATCH 16/17] feat(browser): Add navigation `activationStart` timestamp to pageload span (#13658) Add the navigation performance entry `activationStart` property as a span attribute to the pageload span. The attribute is called `performance.activationStart` and it is measured in ms relative to performance.timeOrigin. --- .../suites/tracing/metrics/web-vitals/test.ts | 37 +++++++++++-------- .../tests/transactions.test.ts | 1 + .../src/metrics/browserMetrics.ts | 9 +++++ 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/test.ts index 3ff09a2862c5..51a76797a23b 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/test.ts @@ -59,23 +59,30 @@ sentryTest('paint web vitals values are greater than TTFB', async ({ browserName expect(fpValue).toBeGreaterThanOrEqual(ttfbValue!); }); -sentryTest('captures time origin as span attribute', async ({ getLocalTestPath, page }) => { - // Only run in chromium to ensure all vitals are present - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } +sentryTest( + 'captures time origin and navigation activationStart as span attributes', + async ({ getLocalTestPath, page }) => { + // Only run in chromium to ensure all vitals are present + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } - const url = await getLocalTestPath({ testDir: __dirname }); - const [eventData] = await Promise.all([getFirstSentryEnvelopeRequest(page), page.goto(url)]); + const url = await getLocalTestPath({ testDir: __dirname }); + const [eventData] = await Promise.all([getFirstSentryEnvelopeRequest(page), page.goto(url)]); - const timeOriginAttribute = eventData.contexts?.trace?.data?.['performance.timeOrigin']; - const transactionStartTimestamp = eventData.start_timestamp; + const timeOriginAttribute = eventData.contexts?.trace?.data?.['performance.timeOrigin']; + const activationStart = eventData.contexts?.trace?.data?.['performance.activationStart']; - expect(timeOriginAttribute).toBeDefined(); - expect(transactionStartTimestamp).toBeDefined(); + const transactionStartTimestamp = eventData.start_timestamp; - const delta = Math.abs(transactionStartTimestamp! - timeOriginAttribute); + expect(timeOriginAttribute).toBeDefined(); + expect(transactionStartTimestamp).toBeDefined(); - // The delta should be less than 1ms if this flakes, we should increase the threshold - expect(delta).toBeLessThanOrEqual(1); -}); + const delta = Math.abs(transactionStartTimestamp! - timeOriginAttribute); + + // The delta should be less than 1ms if this flakes, we should increase the threshold + expect(delta).toBeLessThanOrEqual(1); + + expect(activationStart).toBeGreaterThanOrEqual(0); + }, +); diff --git a/dev-packages/e2e-tests/test-applications/react-create-hash-router/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/react-create-hash-router/tests/transactions.test.ts index 861b6c420fbb..fa6b40c6a49d 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-hash-router/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-create-hash-router/tests/transactions.test.ts @@ -23,6 +23,7 @@ test('Captures a pageload transaction', async ({ page }) => { 'sentry.sample_rate': 1, 'sentry.source': 'route', 'performance.timeOrigin': expect.any(Number), + 'performance.activationStart': expect.any(Number), }, op: 'pageload', span_id: expect.any(String), diff --git a/packages/browser-utils/src/metrics/browserMetrics.ts b/packages/browser-utils/src/metrics/browserMetrics.ts index 066eba1e6839..92fe66a832ee 100644 --- a/packages/browser-utils/src/metrics/browserMetrics.ts +++ b/packages/browser-utils/src/metrics/browserMetrics.ts @@ -17,6 +17,7 @@ import { addTtfbInstrumentationHandler, } from './instrument'; import { getBrowserPerformanceAPI, isMeasurementValue, msToSec, startAndEndSpan } from './utils'; +import { getActivationStart } from './web-vitals/lib/getActivationStart'; import { getNavigationEntry } from './web-vitals/lib/getNavigationEntry'; import { getVisibilityWatcher } from './web-vitals/lib/getVisibilityWatcher'; @@ -383,6 +384,14 @@ export function addPerformanceEntries(span: Span, options: AddPerformanceEntries // Set timeOrigin which denotes the timestamp which to base the LCP/FCP/FP/TTFB measurements on span.setAttribute('performance.timeOrigin', timeOrigin); + // In prerendering scenarios, where a page might be prefetched and pre-rendered before the user clicks the link, + // the navigation starts earlier than when the user clicks it. Web Vitals should always be based on the + // user-perceived time, so they are not reported from the actual start of the navigation, but rather from the + // time where the user actively started the navigation, for example by clicking a link. + // This is user action is called "activation" and the time between navigation and activation is stored in + // the `activationStart` attribute of the "navigation" PerformanceEntry. + span.setAttribute('performance.activationStart', getActivationStart()); + _setWebVitalAttributes(span); } From 8d2e1890b488544195bde899abc3d3ceaa722514 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Fri, 13 Sep 2024 14:12:41 +0200 Subject: [PATCH 17/17] chore: Add cloudflare sdk to main README (#13688) Co-authored-by: IEatBeans <75859301+KyGuy2002@users.noreply.github.com> --- CHANGELOG.md | 2 ++ README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bf504ce80b8..45e39dcaec80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +Work in this release was contributed by @KyGuy2002. Thank you for your contribution! + ## 8.30.0 ### Important Changes diff --git a/README.md b/README.md index 3309f0521986..be275766a156 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,8 @@ package. Please refer to the README and instructions of those SDKs for more deta for native crashes - [`@sentry/bun`](https://github.com/getsentry/sentry-javascript/tree/master/packages/bun): SDK for Bun - [`@sentry/deno`](https://github.com/getsentry/sentry-javascript/tree/master/packages/deno): SDK for Deno +- [`@sentry/cloudflare`](https://github.com/getsentry/sentry-javascript/tree/master/packages/cloudflare): SDK for + Cloudflare ## Version Support Policy