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/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 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/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/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/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/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/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", 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", 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", 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", 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" }, 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/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" }, 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/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/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/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/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/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/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); } 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; }; } diff --git a/packages/node/src/integrations/local-variables/worker.ts b/packages/node/src/integrations/local-variables/worker.ts index eb4fee87947c..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)); } } @@ -118,7 +119,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 +159,10 @@ async function startDebugger(): Promise { }, 1_000); } }, - _ => { - // ignore any errors + async _ => { + if (isPaused) { + await session.post('Debugger.resume'); + } }, ); }); 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/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[]; diff --git a/packages/nuxt/src/module.ts b/packages/nuxt/src/module.ts index faa48e5c3c26..f43f30d7e5ee 100644 --- a/packages/nuxt/src/module.ts +++ b/packages/nuxt/src/module.ts @@ -1,6 +1,7 @@ import { addPlugin, addPluginTemplate, addServerPlugin, createResolver, defineNuxtModule } from '@nuxt/kit'; +import { consoleSandbox } from '@sentry/utils'; 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 +63,22 @@ 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) { + 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 dc1fc21dd6bd..60f2452cde5b 100644 --- a/packages/nuxt/src/vite/addServerConfig.ts +++ b/packages/nuxt/src/vite/addServerConfig.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; -import * as path from 'path'; import { createResolver } from '@nuxt/kit'; import type { Nuxt } from '@nuxt/schema'; +import { consoleSandbox } from '@sentry/utils'; import type { SentryNuxtModuleOptions } from '../common/types'; /** @@ -30,28 +30,72 @@ 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); 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) { + 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, + ); + }); + } + } + }); + }); +} + +/** + * 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) { + 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) { + 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, + `[Sentry] An error occurred when trying to add the Sentry import to the server entry file "\`${entryFilePath}\`":`, + err, ); - } + }); } - }); + } }); } 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, 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/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]; 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/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]'); }); 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; } 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"