From 7cc5b4e7da0cc47ad7ccc9c861e1992cf768c4c6 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Thu, 27 Jun 2024 14:05:47 +0200 Subject: [PATCH 01/30] ref(bug-template): Add reproduction example step (#12671) Additional Changes: - Updating the versions to more recent ones. - Adding `@sentry/solid` --------- Co-authored-by: Francesco Novy --- .github/ISSUE_TEMPLATE/bug.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 773e13ac54fe..d79eb9f1333e 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -43,6 +43,7 @@ body: - '@sentry/node' - '@sentry/react' - '@sentry/remix' + - '@sentry/solid' - '@sentry/svelte' - '@sentry/sveltekit' - '@sentry/vue' @@ -56,7 +57,7 @@ body: attributes: label: SDK Version description: What version of the SDK are you using? - placeholder: ex. 7.8.0 + placeholder: ex. 8.10.0 validations: required: true - type: input @@ -66,7 +67,7 @@ body: description: If you're using one of our framework-specific SDKs (`@sentry/react`, for example), what version of the _framework_ are you using? - placeholder: ex. React 17.0.0 + placeholder: ex. React 18.3.0 or Next 14.0.0 - type: input id: link-to-sentry attributes: @@ -78,8 +79,10 @@ body: - type: textarea id: sdk-setup attributes: - label: SDK Setup - description: How do you set up your Sentry SDK? Please show us your `Sentry.init` options. + label: SDK Setup/Reproduction Example + description: + How do you set up your Sentry SDK? Please show us your `Sentry.init` code. + Or even better—share a link to a reproduction example. placeholder: |- ```javascript Sentry.init({ From 96b3f2142503feb69b768b9082a2284b44cd3b9c Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Thu, 27 Jun 2024 17:08:22 +0200 Subject: [PATCH 02/30] feat(bug-template): Add `@sentry/nest` (#12679) ref https://github.com/getsentry/sentry-javascript/pull/12671 --- .github/ISSUE_TEMPLATE/bug.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index d79eb9f1333e..68842b96e3e7 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -39,6 +39,7 @@ body: - '@sentry/ember' - '@sentry/gatsby' - '@sentry/google-cloud-serverless' + - '@sentry/nestjs' - '@sentry/nextjs' - '@sentry/node' - '@sentry/react' From 36a15fa12c7c569163a1da77673a001c6b7a7130 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 27 Jun 2024 14:03:57 -0400 Subject: [PATCH 03/30] chore: Remove @codecov/rollup-plugin (#12665) --- .../rollup-utils/plugins/npmPlugins.mjs | 1 - package.json | 1 - yarn.lock | 43 ++++--------------- 3 files changed, 8 insertions(+), 37 deletions(-) diff --git a/dev-packages/rollup-utils/plugins/npmPlugins.mjs b/dev-packages/rollup-utils/plugins/npmPlugins.mjs index fd736d702e07..6597e2244ab8 100644 --- a/dev-packages/rollup-utils/plugins/npmPlugins.mjs +++ b/dev-packages/rollup-utils/plugins/npmPlugins.mjs @@ -10,7 +10,6 @@ import * as fs from 'fs'; import * as path from 'path'; -import { codecovRollupPlugin } from '@codecov/rollup-plugin'; import json from '@rollup/plugin-json'; import replace from '@rollup/plugin-replace'; import cleanup from 'rollup-plugin-cleanup'; diff --git a/package.json b/package.json index eab00cafbd18..f9583d6074ec 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,6 @@ ], "devDependencies": { "@biomejs/biome": "^1.4.0", - "@codecov/rollup-plugin": "0.0.1-beta.5", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-esm-shim": "^0.1.5", "@rollup/plugin-json": "^6.1.0", diff --git a/yarn.lock b/yarn.lock index 0461250144d3..83754bf8c377 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3840,23 +3840,6 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@codecov/bundler-plugin-core@^0.0.1-beta.5": - version "0.0.1-beta.5" - resolved "https://registry.yarnpkg.com/@codecov/bundler-plugin-core/-/bundler-plugin-core-0.0.1-beta.5.tgz#f516c080abe4c99fb5cfdc7d39513b277fac2bae" - integrity sha512-eltwv1aRJdwonlTD47jQ2GYdKZfMv3jCviDui32JOu12ZAiI3WdJCtjWF9zxpmnHEZ30pn0WbeZir+sXSFLJHw== - dependencies: - chalk "4.1.2" - semver "^7.5.4" - unplugin "^1.6.0" - zod "^3.22.4" - -"@codecov/rollup-plugin@0.0.1-beta.5": - version "0.0.1-beta.5" - resolved "https://registry.yarnpkg.com/@codecov/rollup-plugin/-/rollup-plugin-0.0.1-beta.5.tgz#cca4c68989efbcf4e2dbe3db5e9a5bf5b7501fe2" - integrity sha512-3x6vmt59Hgfpr8jroB7LYa2XX+kFds8HaWsUTBX08sJ2bCw+BzlW1Mnc+IdxBdMtjo1jkYEceRvfwQ0X2LK4ZQ== - dependencies: - "@codecov/bundler-plugin-core" "^0.0.1-beta.5" - "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -13985,14 +13968,6 @@ chalk@4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@4.1.2, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chalk@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -14004,6 +13979,14 @@ chalk@^1.0.0: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^5.0.0, chalk@^5.2.0, chalk@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" @@ -33307,16 +33290,6 @@ unplugin@^1.10.0, unplugin@^1.10.1, unplugin@^1.3.1, unplugin@^1.5.0, unplugin@^ webpack-sources "^3.2.3" webpack-virtual-modules "^0.6.1" -unplugin@^1.6.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-1.10.0.tgz#9cb8140f61e3fbcf27c7c38d305e9d62d5dbbf0b" - integrity sha512-CuZtvvO8ua2Wl+9q2jEaqH6m3DoQ38N7pvBYQbbaeNlWGvK2l6GHiKi29aIHDPoSxdUzQ7Unevf1/ugil5X6Pg== - dependencies: - acorn "^8.11.3" - chokidar "^3.6.0" - webpack-sources "^3.2.3" - webpack-virtual-modules "^0.6.1" - unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" From f91b1334574d021c1b87d89015effec9b52ca0b9 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 27 Jun 2024 16:20:29 -0400 Subject: [PATCH 04/30] fix(browser): Make sure measure spans have valid start timestamps (#12648) Co-authored-by: Francesco Novy --- .../metrics/pageload-measure-spans/init.js | 21 ++++++ .../metrics/pageload-measure-spans/test.ts | 35 ++++++++++ .../src/metrics/browserMetrics.ts | 66 +++++++++++-------- 3 files changed, 95 insertions(+), 27 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/tracing/metrics/pageload-measure-spans/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/metrics/pageload-measure-spans/test.ts diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/pageload-measure-spans/init.js b/dev-packages/browser-integration-tests/suites/tracing/metrics/pageload-measure-spans/init.js new file mode 100644 index 000000000000..d34167f7b256 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/pageload-measure-spans/init.js @@ -0,0 +1,21 @@ +// Add measure before SDK initializes +const end = performance.now(); +performance.measure('Next.js-before-hydration', { + duration: 1000, + end, +}); + +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + debug: true, + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + idleTimeout: 9000, + }), + ], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/pageload-measure-spans/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/pageload-measure-spans/test.ts new file mode 100644 index 000000000000..9209e8ca5c32 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/pageload-measure-spans/test.ts @@ -0,0 +1,35 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; + +// Validation test for https://github.com/getsentry/sentry-javascript/issues/12281 +sentryTest('should add browser-related spans to pageload transaction', async ({ getLocalTestPath, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestPath({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + const browserSpans = eventData.spans?.filter(({ op }) => op === 'browser'); + + // Spans `domContentLoadedEvent`, `connect`, `cache` and `DNS` are not + // always inside `pageload` transaction. + expect(browserSpans?.length).toBeGreaterThanOrEqual(4); + + const requestSpan = browserSpans!.find(({ description }) => description === 'request'); + expect(requestSpan).toBeDefined(); + + const measureSpan = eventData.spans?.find(({ op }) => op === 'measure'); + expect(measureSpan).toBeDefined(); + + expect(requestSpan!.start_timestamp).toBeLessThanOrEqual(measureSpan!.start_timestamp); + expect(measureSpan?.data).toEqual({ + 'sentry.browser.measure_happened_before_request': true, + 'sentry.browser.measure_start_time': expect.any(Number), + 'sentry.op': 'measure', + 'sentry.origin': 'auto.resource.browser.metrics', + }); +}); diff --git a/packages/browser-utils/src/metrics/browserMetrics.ts b/packages/browser-utils/src/metrics/browserMetrics.ts index 6999641a641f..4e473e42ea47 100644 --- a/packages/browser-utils/src/metrics/browserMetrics.ts +++ b/packages/browser-utils/src/metrics/browserMetrics.ts @@ -342,15 +342,34 @@ export function _addMeasureSpans( duration: number, timeOrigin: number, ): number { - const measureStartTimestamp = timeOrigin + startTime; - const measureEndTimestamp = measureStartTimestamp + duration; + const navEntry = getNavigationEntry(); + const requestTime = msToSec(navEntry ? navEntry.requestStart : 0); + // Because performance.measure accepts arbitrary timestamps it can produce + // spans that happen before the browser even makes a request for the page. + // + // An example of this is the automatically generated Next.js-before-hydration + // spans created by the Next.js framework. + // + // To prevent this we will pin the start timestamp to the request start time + // This does make duration inaccruate, so if this does happen, we will add + // an attribute to the span + const measureStartTimestamp = timeOrigin + Math.max(startTime, requestTime); + const startTimeStamp = timeOrigin + startTime; + const measureEndTimestamp = startTimeStamp + duration; + + const attributes: SpanAttributes = { + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics', + }; + + if (measureStartTimestamp !== startTimeStamp) { + attributes['sentry.browser.measure_happened_before_request'] = true; + attributes['sentry.browser.measure_start_time'] = measureStartTimestamp; + } startAndEndSpan(span, measureStartTimestamp, measureEndTimestamp, { name: entry.name as string, op: entry.entryType as string, - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.resource.browser.metrics', - }, + attributes, }); return measureStartTimestamp; @@ -395,36 +414,29 @@ function _addPerformanceNavigationTiming( /** Create request and response related spans */ // eslint-disable-next-line @typescript-eslint/no-explicit-any function _addRequest(span: Span, entry: Record, timeOrigin: number): void { + const requestStartTimestamp = timeOrigin + msToSec(entry.requestStart as number); + const responseEndTimestamp = timeOrigin + msToSec(entry.responseEnd as number); + const responseStartTimestamp = timeOrigin + msToSec(entry.responseStart as number); if (entry.responseEnd) { // It is possible that we are collecting these metrics when the page hasn't finished loading yet, for example when the HTML slowly streams in. // In this case, ie. when the document request hasn't finished yet, `entry.responseEnd` will be 0. // In order not to produce faulty spans, where the end timestamp is before the start timestamp, we will only collect // these spans when the responseEnd value is available. The backend (Relay) would drop the entire span if it contained faulty spans. - startAndEndSpan( - span, - timeOrigin + msToSec(entry.requestStart as number), - timeOrigin + msToSec(entry.responseEnd as number), - { - op: 'browser', - name: 'request', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics', - }, + startAndEndSpan(span, requestStartTimestamp, responseEndTimestamp, { + op: 'browser', + name: 'request', + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics', }, - ); + }); - startAndEndSpan( - span, - timeOrigin + msToSec(entry.responseStart as number), - timeOrigin + msToSec(entry.responseEnd as number), - { - op: 'browser', - name: 'response', - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics', - }, + startAndEndSpan(span, responseStartTimestamp, responseEndTimestamp, { + op: 'browser', + name: 'response', + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics', }, - ); + }); } } From c7b8503018f9384bb4f68a2c3f86c43956fa8380 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Fri, 28 Jun 2024 03:19:30 -0400 Subject: [PATCH 05/30] chore: Remove `es-check` from deps (#12686) ref: https://github.com/getsentry/sentry-javascript/pull/12644 We don't use `es-check` anymore, we can remove it. It was previously used to validate our es5 builds, but we don't target that anymore. Extracted from the previous PR because 1 big change was breaking CI and I couldn't figure out the root cause. --- package.json | 1 - yarn.lock | 148 +++++---------------------------------------------- 2 files changed, 13 insertions(+), 136 deletions(-) diff --git a/package.json b/package.json index f9583d6074ec..41181d7a0ffd 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,6 @@ "codecov": "^3.6.5", "deepmerge": "^4.2.2", "downlevel-dts": "~0.11.0", - "es-check": "7.1.0", "eslint": "7.32.0", "jest": "^27.5.1", "jest-environment-node": "^27.5.1", diff --git a/yarn.lock b/yarn.lock index 83754bf8c377..3381d4487950 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3958,15 +3958,6 @@ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz#2cbcf822bf3764c9658c4d2e568bd0c0cb748016" integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw== -"@dabh/diagnostics@^2.0.2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" - integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== - dependencies: - colorspace "1.1.x" - enabled "2.0.x" - kuler "^2.0.0" - "@dependents/detective-less@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@dependents/detective-less/-/detective-less-4.1.0.tgz#4a979ee7a6a79eb33602862d6a1263e30f98002e" @@ -9450,6 +9441,7 @@ "@types/unist" "*" "@types/history-4@npm:@types/history@4.7.8", "@types/history-5@npm:@types/history@4.7.8", "@types/history@*": + name "@types/history-4" version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== @@ -9963,11 +9955,6 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== -"@types/triple-beam@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.2.tgz#38ecb64f01aa0d02b7c8f4222d7c38af6316fef8" - integrity sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g== - "@types/unist@*", "@types/unist@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.0.tgz#988ae8af1e5239e89f9fbb1ade4c935f4eeedf9a" @@ -11291,11 +11278,6 @@ acorn@8.12.0, acorn@^8.6.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c" integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== -acorn@8.8.2, acorn@^8.8.1, acorn@^8.8.2: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - acorn@^6.4.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" @@ -11321,6 +11303,11 @@ acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.0, acorn@^8.7.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== +acorn@^8.8.1, acorn@^8.8.2: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + acorn@^8.9.0: version "8.11.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b" @@ -14399,7 +14386,7 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.9.0, color-convert@^1.9.3: +color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -14423,7 +14410,7 @@ color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.6.0, color-string@^1.9.0: +color-string@^1.9.0: version "1.9.1" resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== @@ -14436,14 +14423,6 @@ color-support@^1.1.2, color-support@^1.1.3: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -color@^3.1.3: - version "3.2.1" - resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" - integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== - dependencies: - color-convert "^1.9.3" - color-string "^1.6.0" - color@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" @@ -14472,14 +14451,6 @@ colors@^1.4.0: resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== -colorspace@1.1.x: - version "1.1.4" - resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" - integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== - dependencies: - color "^3.1.3" - text-hex "1.0.x" - columnify@1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" @@ -14500,11 +14471,6 @@ comma-separated-tokens@^2.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== -commander@10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.0.tgz#71797971162cd3cf65f0b9d24eb28f8d303acdf1" - integrity sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA== - commander@2.8.x: version "2.8.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" @@ -16971,11 +16937,6 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -enabled@2.0.x: - version "2.0.0" - resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" - integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== - encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -17164,17 +17125,6 @@ es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.19.0, es- string.prototype.trimstart "^1.0.5" unbox-primitive "^1.0.2" -es-check@7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/es-check/-/es-check-7.1.0.tgz#1015db640a7b785ff4098baf2e0791c070a25964" - integrity sha512-t099vm9tNqNHF28Q/mRcqYxmkbkoo/Qu2ZI5/D+eFeqNUjI3jwkIyHyexXiAtstbZ1FQELi0QCuUaYCtiffi4Q== - dependencies: - acorn "8.8.2" - commander "10.0.0" - fast-glob "^3.2.12" - supports-color "^8.1.1" - winston "^3.8.2" - es-define-property@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" @@ -18393,7 +18343,7 @@ fast-glob@3.2.7: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9: +fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -18532,11 +18482,6 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -fecha@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" - integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== - fflate@0.8.1, fflate@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.1.tgz#1ed92270674d2ad3c73f077cd0acf26486dae6c9" @@ -18848,11 +18793,6 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" -fn.name@1.x.x: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" - integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== - focus-trap@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-7.5.4.tgz#6c4e342fe1dae6add9c2aa332a6e7a0bbd495ba2" @@ -22836,11 +22776,6 @@ kolorist@^1.8.0: resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c" integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== -kuler@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" - integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== - language-subtag-registry@~0.3.2: version "0.3.22" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" @@ -23515,18 +23450,6 @@ log-symbols@^5.1.0: chalk "^5.0.0" is-unicode-supported "^1.1.0" -logform@^2.3.2, logform@^2.4.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.5.1.tgz#44c77c34becd71b3a42a3970c77929e52c6ed48b" - integrity sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg== - dependencies: - "@colors/colors" "1.5.0" - "@types/triple-beam" "^1.3.2" - fecha "^4.2.0" - ms "^2.1.1" - safe-stable-stringify "^2.3.1" - triple-beam "^1.3.0" - loglevel@^1.6.8: version "1.8.0" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114" @@ -26502,13 +26425,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -one-time@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" - integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== - dependencies: - fn.name "1.x.x" - onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -28943,6 +28859,7 @@ react-is@^18.0.0: "@remix-run/router" "1.0.2" "react-router-6@npm:react-router@6.3.0", react-router@6.3.0: + name react-router-6 version "6.3.0" resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557" integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ== @@ -31299,11 +31216,6 @@ ssri@^9.0.0, ssri@^9.0.1: dependencies: minipass "^3.1.1" -stack-trace@0.0.x: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== - stack-utils@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" @@ -31467,6 +31379,7 @@ string-template@~0.2.1: integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= "string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + name string-width-cjs version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -31803,7 +31716,7 @@ supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0, supports-color@^8.1.1: +supports-color@^8.0.0: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -32219,11 +32132,6 @@ text-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== -text-hex@1.0.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" - integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== - text-table@0.2.0, text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -32532,11 +32440,6 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== -triple-beam@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" - integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== - trough@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876" @@ -34619,32 +34522,6 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== -winston-transport@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" - integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== - dependencies: - logform "^2.3.2" - readable-stream "^3.6.0" - triple-beam "^1.3.0" - -winston@^3.8.2: - version "3.8.2" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.8.2.tgz#56e16b34022eb4cff2638196d9646d7430fdad50" - integrity sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew== - dependencies: - "@colors/colors" "1.5.0" - "@dabh/diagnostics" "^2.0.2" - async "^3.2.3" - is-stream "^2.0.0" - logform "^2.4.0" - one-time "^1.0.0" - readable-stream "^3.4.0" - safe-stable-stringify "^2.3.1" - stack-trace "0.0.x" - triple-beam "^1.3.0" - winston-transport "^4.5.0" - word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.4" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" @@ -34692,6 +34569,7 @@ workerpool@^6.4.0: integrity sha512-i3KR1mQMNwY2wx20ozq2EjISGtQWDIfV56We+yGJ5yDs8jTwQiLLaqHlkBHITlCuJnYlVRmXegxFxZg7gqI++A== "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + name wrap-ansi-cjs version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 5eafa401c076796fba7663dad9ca254ab6f1972f Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Fri, 28 Jun 2024 11:49:14 +0200 Subject: [PATCH 06/30] fix(nextjs): Attempt to ignore critical dependency warnings (#12694) --- packages/nextjs/src/config/types.ts | 18 +++++++++++++++++- packages/nextjs/src/config/webpack.ts | 26 ++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index b84c55e5611f..883ba3c26d41 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -404,6 +404,22 @@ export type NextConfigFunction = ( * Webpack config */ +// Note: The interface for `ignoreWarnings` is larger but we only need this. See https://webpack.js.org/configuration/other-options/#ignorewarnings +export type IgnoreWarningsOption = ( + | { module?: RegExp; message?: RegExp } + | (( + webpackError: { + module?: { + readableIdentifier: (requestShortener: unknown) => string; + }; + message: string; + }, + compilation: { + requestShortener: unknown; + }, + ) => boolean) +)[]; + // The two possible formats for providing custom webpack config in `next.config.js` export type WebpackConfigFunction = (config: WebpackConfigObject, options: BuildContext) => WebpackConfigObject; export type WebpackConfigObject = { @@ -413,7 +429,7 @@ export type WebpackConfigObject = { output: { filename: string; path: string }; target: string; context: string; - ignoreWarnings?: { module?: RegExp }[]; // Note: The interface for `ignoreWarnings` is larger but we only need this. See https://webpack.js.org/configuration/other-options/#ignorewarnings + ignoreWarnings?: IgnoreWarningsOption; resolve?: { modules?: string[]; alias?: { [key: string]: string | boolean }; diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index eece7ff30305..4002db18f295 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -14,6 +14,7 @@ import type { VercelCronsConfig } from '../common/types'; import type { BuildContext, EntryPropertyObject, + IgnoreWarningsOption, NextConfigObject, SentryBuildOptions, WebpackConfigFunction, @@ -72,9 +73,7 @@ export function constructWebpackConfigFunction( // Add a loader which will inject code that sets global values addValueInjectionLoader(newConfig, userNextConfig, userSentryOptions, buildContext); - if (isServer) { - addOtelWarningIgnoreRule(newConfig); - } + addOtelWarningIgnoreRule(newConfig); let pagesDirPath: string | undefined; const maybePagesDirPath = path.join(projectDir, 'pages'); @@ -668,9 +667,28 @@ function getRequestAsyncStorageModuleLocation( function addOtelWarningIgnoreRule(newConfig: WebpackConfigObjectWithModuleRules): void { const ignoreRules = [ + // Inspired by @matmannion: https://github.com/getsentry/sentry-javascript/issues/12077#issuecomment-2180307072 + (warning, compilation) => { + // This is wapped in try-catch because we are vendoring types for this hook and we can't be 100% sure that we are accessing API that is there + try { + if (!warning.module) { + return false; + } + + const isDependencyThatMayRaiseCriticalDependencyMessage = + /@opentelemetry\/instrumentation/.test(warning.module.readableIdentifier(compilation.requestShortener)) || + /@prisma\/instrumentation/.test(warning.module.readableIdentifier(compilation.requestShortener)); + const isCriticalDependencyMessage = /Critical dependency/.test(warning.message); + + return isDependencyThatMayRaiseCriticalDependencyMessage && isCriticalDependencyMessage; + } catch { + return false; + } + }, + // We provide these objects in addition to the hook above to provide redundancy in case the hook fails. { module: /@opentelemetry\/instrumentation/, message: /Critical dependency/ }, { module: /@prisma\/instrumentation/, message: /Critical dependency/ }, - ]; + ] satisfies IgnoreWarningsOption; if (newConfig.ignoreWarnings === undefined) { newConfig.ignoreWarnings = ignoreRules; From c548c3c7f9793f6a96c16acf96e0a0a8da574346 Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Fri, 28 Jun 2024 17:30:10 +0200 Subject: [PATCH 07/30] feat(node): Add `registerEsmLoaderHooks` option (#12684) Currently the only way to disable ESM loader hook registration is to set: ```ts globalThis._sentryEsmLoaderHookRegistered = true; ``` After this PR, you can set the new `registerEsmLoaderHooks` option to `false`: ```ts import * as Sentry from '@sentry/node'; Sentry.init({ dsn: '__DSN__', registerEsmLoaderHooks: false, }); ``` --------- Co-authored-by: Francesco Novy --- packages/node/src/sdk/index.ts | 2 +- packages/node/src/types.ts | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/node/src/sdk/index.ts b/packages/node/src/sdk/index.ts index 746136c03022..4c30ba2bd2cf 100644 --- a/packages/node/src/sdk/index.ts +++ b/packages/node/src/sdk/index.ts @@ -130,7 +130,7 @@ function _init( } } - if (!isCjs()) { + if (!isCjs() && options.registerEsmLoaderHooks !== false) { maybeInitializeEsmLoader(); } diff --git a/packages/node/src/types.ts b/packages/node/src/types.ts index 2c00302e2e64..882114a013f9 100644 --- a/packages/node/src/types.ts +++ b/packages/node/src/types.ts @@ -85,6 +85,16 @@ export interface BaseNodeOptions { */ maxSpanWaitDuration?: number; + /** + * Whether to register ESM loader hooks to automatically instrument libraries. + * This is necessary to auto instrument libraries that are loaded via ESM imports, but might it can cause issues + * with certain libraries. If you run into problems running your app with this enabled, + * please raise an issue in https://github.com/getsentry/sentry-javascript. + * + * Defaults to `true`. + */ + registerEsmLoaderHooks?: boolean; + /** Callback that is executed when a fatal global error occurs. */ onFatalError?(this: void, error: Error): void; } From 24dfc666fae9c2f0ce827acf5ac5c44917d15e99 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Fri, 28 Jun 2024 12:41:49 -0400 Subject: [PATCH 08/30] chore: Remove replace-in-file and inquirer (#12687) ref: https://github.com/getsentry/sentry-javascript/pull/12644 We can remove `replace-in-file` with some built-in functions and clean up our lockfile even more. Extracted from the previous PR because 1 big change was breaking CI and I couldn't figure out the root cause. --- .../bundle-analyzer-scenarios/package.json | 7 +- .../bundle-analyzer-scenarios/webpack.cjs | 35 ++-- package.json | 10 +- scripts/versionbump.js | 44 +++-- yarn.lock | 167 ++++++------------ 5 files changed, 108 insertions(+), 155 deletions(-) diff --git a/dev-packages/bundle-analyzer-scenarios/package.json b/dev-packages/bundle-analyzer-scenarios/package.json index 2ff890293166..38e946577def 100644 --- a/dev-packages/bundle-analyzer-scenarios/package.json +++ b/dev-packages/bundle-analyzer-scenarios/package.json @@ -8,10 +8,9 @@ "license": "MIT", "private": true, "dependencies": { - "html-webpack-plugin": "^5.5.0", - "inquirer": "^8.2.0", - "webpack": "^5.76.0", - "webpack-bundle-analyzer": "^4.5.0" + "html-webpack-plugin": "^5.6.0", + "webpack": "^5.92.1", + "webpack-bundle-analyzer": "^4.10.2" }, "scripts": { "analyze": "node webpack.cjs" diff --git a/dev-packages/bundle-analyzer-scenarios/webpack.cjs b/dev-packages/bundle-analyzer-scenarios/webpack.cjs index aac95e59348a..f5874a607473 100644 --- a/dev-packages/bundle-analyzer-scenarios/webpack.cjs +++ b/dev-packages/bundle-analyzer-scenarios/webpack.cjs @@ -1,7 +1,7 @@ -const path = require('path'); -const { promises } = require('fs'); +const path = require('node:path'); +const { promises } = require('node:fs'); +const { parseArgs } = require('node:util'); -const inquirer = require('inquirer'); const webpack = require('webpack'); const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const HtmlWebpackPlugin = require('html-webpack-plugin'); @@ -9,20 +9,25 @@ const HtmlWebpackPlugin = require('html-webpack-plugin'); async function init() { const scenarios = await getScenariosFromDirectories(); - const answers = await inquirer.prompt([ - { - type: 'rawlist', - name: 'scenario', - message: 'Which scenario you want to run?', - choices: scenarios, - pageSize: scenarios.length, - loop: false, - }, - ]); + const { values } = parseArgs({ + args: process.argv.slice(2), + options: { scenario: { type: 'string', short: 's' }, list: { type: 'boolean', short: 'l' } }, + }); + + if (values.list) { + console.log('Available scenarios:', scenarios); + process.exit(0); + } + + if (!scenarios.some(scenario => scenario === values.scenario)) { + console.error('Invalid scenario:', values.scenario); + console.error('Available scenarios:', scenarios); + process.exit(1); + } - console.log(`Bundling scenario: ${answers.scenario}`); + console.log(`Bundling scenario: ${values.scenario}`); - await runWebpack(answers.scenario); + await runWebpack(values.scenario); } async function runWebpack(scenario) { diff --git a/package.json b/package.json index 41181d7a0ffd..a1ff37a848a6 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,6 @@ "nodemon": "^2.0.16", "npm-run-all": "^4.1.5", "prettier": "^3.1.1", - "replace-in-file": "^4.0.0", "rimraf": "^3.0.2", "rollup": "^4.13.0", "rollup-plugin-cleanup": "^3.2.1", @@ -134,8 +133,15 @@ "vitest": "^1.6.0", "yalc": "^1.0.0-pre.53" }, + "//_resolutions_comment": [ + "Because new versions of strip-ansi, string-width, and wrap-ansi are ESM only packages,", + "we need to resolve them to the CommonJS versions.", + "This is a temporary solution until we can upgrade to a version of lerna that supports ESM packages" + ], "resolutions": { - "gauge/strip-ansi": "6.0.1" + "gauge/strip-ansi": "6.0.1", + "wide-align/string-width": "4.2.3", + "cliui/wrap-ansi": "7.0.0" }, "version": "0.0.0", "name": "sentry-javascript", diff --git a/scripts/versionbump.js b/scripts/versionbump.js index e0edddfbca9c..931df2a7829c 100644 --- a/scripts/versionbump.js +++ b/scripts/versionbump.js @@ -1,21 +1,31 @@ -const replace = require('replace-in-file'); +const { readFile, writeFile } = require('node:fs').promises; const pjson = require(`${process.cwd()}/package.json`); -const files = process.argv.slice(2); -if (files.length === 0) { - console.error('Please provide files to bump'); - process.exit(1); -} +const REPLACE_REGEX = /\d+\.\d+.\d+(?:-\w+(?:\.\w+)?)?/g; + +async function run() { + const files = process.argv.slice(2); + if (files.length === 0) { + // eslint-disable-next-line no-console + console.error('[versionbump] Please provide files to bump'); + process.exit(1); + } -replace({ - files: files, - from: /\d+\.\d+.\d+(?:-\w+(?:\.\w+)?)?/g, - to: pjson.version, -}) - .then(changedFiles => { - console.log('Modified files:', changedFiles.join(', ')); - }) - .catch(error => { - console.error('Error occurred:', error); + try { + await Promise.all( + files.map(async file => { + const data = String(await readFile(file, 'utf8')); + await writeFile(file, data.replace(REPLACE_REGEX, pjson.version)); + }), + ); + + // eslint-disable-next-line no-console + console.log(`[versionbump] Bumped version for ${files.join(', ')}`); + } catch (error) { + // eslint-disable-next-line no-console + console.error('[versionbump] Error occurred:', error); process.exit(1); - }); + } +} + +run(); diff --git a/yarn.lock b/yarn.lock index 3381d4487950..b8bf4eeeb15c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13855,7 +13855,7 @@ camelcase@5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== -camelcase@^5.0.0, camelcase@^5.3.1: +camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== @@ -14278,15 +14278,6 @@ clipboardy@^4.0.0: is-wsl "^3.1.0" is64bit "^2.0.0" -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -15434,7 +15425,7 @@ decamelize-keys@^1.1.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.1.0, decamelize@^1.2.0: +decamelize@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -16988,7 +16979,7 @@ enhanced-resolve@^4.5.0: memory-fs "^0.5.0" tapable "^1.0.0" -enhanced-resolve@^5.10.0, enhanced-resolve@^5.16.0: +enhanced-resolve@^5.10.0: version "5.16.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz#65ec88778083056cb32487faa9aef82ed0864787" integrity sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA== @@ -17012,6 +17003,14 @@ enhanced-resolve@^5.15.0: graceful-fs "^4.2.4" tapable "^2.2.0" +enhanced-resolve@^5.17.0: + version "5.17.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" + integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + enquirer@^2.3.5, enquirer@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -19183,7 +19182,7 @@ get-amd-module-type@^5.0.1: ast-module-types "^5.0.0" node-source-walk "^6.0.1" -get-caller-file@^2.0.1, get-caller-file@^2.0.5: +get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -20483,6 +20482,17 @@ html-webpack-plugin@^5.5.0: pretty-error "^4.0.0" tapable "^2.0.0" +html-webpack-plugin@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz#50a8fa6709245608cb00e811eacecb8e0d7b7ea0" + integrity sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw== + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + htmlparser2@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" @@ -20962,27 +20972,6 @@ inquirer@^7.0.1: strip-ansi "^6.0.0" through "^2.3.6" -inquirer@^8.2.0: - version "8.2.6" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" - integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.1" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.21" - mute-stream "0.0.8" - ora "^5.4.1" - run-async "^2.4.0" - rxjs "^7.5.5" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - wrap-ansi "^6.0.1" - inquirer@^8.2.4: version "8.2.5" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" @@ -29451,15 +29440,6 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -replace-in-file@^4.0.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/replace-in-file/-/replace-in-file-4.3.1.tgz#c67c92178b38052008e379197cc0d86ca927f7b0" - integrity sha512-FqVvfmpqGTD2JRGI1JjJ86b24P17x/WWwGdxExeyJxnh/2rVQz2+jXfD1507UnnhEQw092X0u0DPCBf1WC4ooQ== - dependencies: - chalk "^2.4.2" - glob "^7.1.6" - yargs "^15.0.2" - require-at@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/require-at/-/require-at-1.0.6.tgz#9eb7e3c5e00727f5a4744070a7f560d4de4f6e6a" @@ -29484,11 +29464,6 @@ require-in-the-middle@^7.1.1: module-details-from-path "^1.0.3" resolve "^1.22.1" -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - requireindex@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef" @@ -31378,8 +31353,7 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: - name string-width-cjs +"string-width-cjs@npm:string-width@^4.2.0", string-width@4.2.3, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -34090,10 +34064,10 @@ webidl-conversions@^7.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -webpack-bundle-analyzer@^4.5.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.1.tgz#84b7473b630a7b8c21c741f81d8fe4593208b454" - integrity sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ== +webpack-bundle-analyzer@^4.10.2: + version "4.10.2" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz#633af2862c213730be3dbdf40456db171b60d5bd" + integrity sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw== dependencies: "@discoveryjs/json-ext" "0.5.7" acorn "^8.0.4" @@ -34103,7 +34077,6 @@ webpack-bundle-analyzer@^4.5.0: escape-string-regexp "^4.0.0" gzip-size "^6.0.0" html-escaper "^2.0.2" - is-plain-object "^5.0.0" opener "^1.5.2" picocolors "^1.0.0" sirv "^2.0.3" @@ -34252,26 +34225,26 @@ webpack@^4.47.0: watchpack "^1.7.4" webpack-sources "^1.4.1" -webpack@^5.76.0: - version "5.91.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.91.0.tgz#ffa92c1c618d18c878f06892bbdc3373c71a01d9" - integrity sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw== +webpack@^5.90.3, webpack@~5.90.3: + version "5.90.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.90.3.tgz#37b8f74d3ded061ba789bb22b31e82eed75bd9ac" + integrity sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.5" - "@webassemblyjs/ast" "^1.12.1" - "@webassemblyjs/wasm-edit" "^1.12.1" - "@webassemblyjs/wasm-parser" "^1.12.1" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" acorn "^8.7.1" acorn-import-assertions "^1.9.0" browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.16.0" + enhanced-resolve "^5.15.0" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.11" + graceful-fs "^4.2.9" json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" @@ -34279,29 +34252,29 @@ webpack@^5.76.0: schema-utils "^3.2.0" tapable "^2.1.1" terser-webpack-plugin "^5.3.10" - watchpack "^2.4.1" + watchpack "^2.4.0" webpack-sources "^3.2.3" -webpack@^5.90.3, webpack@~5.90.3: - version "5.90.3" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.90.3.tgz#37b8f74d3ded061ba789bb22b31e82eed75bd9ac" - integrity sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA== +webpack@^5.92.1: + version "5.92.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.92.1.tgz#eca5c1725b9e189cffbd86e8b6c3c7400efc5788" + integrity sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.5" - "@webassemblyjs/ast" "^1.11.5" - "@webassemblyjs/wasm-edit" "^1.11.5" - "@webassemblyjs/wasm-parser" "^1.11.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" acorn "^8.7.1" - acorn-import-assertions "^1.9.0" + acorn-import-attributes "^1.9.5" browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.15.0" + enhanced-resolve "^5.17.0" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" + graceful-fs "^4.2.11" json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" @@ -34309,7 +34282,7 @@ webpack@^5.90.3, webpack@~5.90.3: schema-utils "^3.2.0" tapable "^2.1.1" terser-webpack-plugin "^5.3.10" - watchpack "^2.4.0" + watchpack "^2.4.1" webpack-sources "^3.2.3" websocket-driver@>=0.5.1, websocket-driver@^0.7.4: @@ -34410,11 +34383,6 @@ which-collection@^1.0.1: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - which-pm-runs@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.1.0.tgz#35ccf7b1a0fce87bd8b92a478c9d045785d3bf35" @@ -34568,8 +34536,7 @@ workerpool@^6.4.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.4.0.tgz#f8d5cfb45fde32fa3b7af72ad617c3369567a462" integrity sha512-i3KR1mQMNwY2wx20ozq2EjISGtQWDIfV56We+yGJ5yDs8jTwQiLLaqHlkBHITlCuJnYlVRmXegxFxZg7gqI++A== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: - name wrap-ansi-cjs +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@7.0.0, wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -34578,15 +34545,6 @@ workerpool@^6.4.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -34791,14 +34749,6 @@ yargs-parser@21.1.1, yargs-parser@^21.0.0, yargs-parser@^21.1.1: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs@16.2.0, yargs@^16.1.1, yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" @@ -34825,23 +34775,6 @@ yargs@17.5.1: y18n "^5.0.5" yargs-parser "^21.0.0" -yargs@^15.0.2: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - yargs@^17.2.1: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" From f4a289d30e1a638f3f979bbd88102db009e748f8 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Mon, 1 Jul 2024 12:44:30 +0200 Subject: [PATCH 09/30] test: Add OTEL E2E test app using sdk-node (#12690) This adds an E2E app using a custom OTEL setup with `@opentelemetry/sdk-node`. It tests that data is sent both to sentry as well as to another OTLP exporter. For this, I adjusted the event proxy code to also allow to spin up a generic event proxy server (which I use for the OTLP exporter). I also rewrote this to use fetch as this is a bit easier to read IMHO. This is a decent first step, we should add at least 2 more E2E test apps IMHO related to OTEL: 1. An app using `@opentelemetry/sdk-trace-node` and some more custom tracing setup (e.g. more instrumentation, custom sampler, ....) 2. An app using `@opentelemetry/sdk-trace-node` that does not use Sentry for performance at all, but only for errors, and only uses OTEL for trace monitoring. Part of https://github.com/getsentry/sentry-javascript/issues/12494 --- .github/workflows/build.yml | 1 + .../node-otel-sdk-node/.gitignore | 1 + .../node-otel-sdk-node/.npmrc | 2 + .../node-otel-sdk-node/package.json | 32 +++ .../node-otel-sdk-node/playwright.config.mjs | 34 +++ .../node-otel-sdk-node/src/app.ts | 53 ++++ .../node-otel-sdk-node/src/instrument.ts | 35 +++ .../node-otel-sdk-node/start-event-proxy.mjs | 6 + .../node-otel-sdk-node/start-otel-proxy.mjs | 6 + .../node-otel-sdk-node/tests/errors.test.ts | 29 +++ .../tests/transactions.test.ts | 213 +++++++++++++++ .../node-otel-sdk-node/tsconfig.json | 10 + .../test-utils/src/event-proxy-server.ts | 244 +++++++++++------- dev-packages/test-utils/src/index.ts | 2 + 14 files changed, 570 insertions(+), 98 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/node-otel-sdk-node/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/node-otel-sdk-node/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/node-otel-sdk-node/package.json create mode 100644 dev-packages/e2e-tests/test-applications/node-otel-sdk-node/playwright.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/node-otel-sdk-node/src/app.ts create mode 100644 dev-packages/e2e-tests/test-applications/node-otel-sdk-node/src/instrument.ts create mode 100644 dev-packages/e2e-tests/test-applications/node-otel-sdk-node/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/node-otel-sdk-node/start-otel-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tests/errors.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tests/transactions.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tsconfig.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 445c57284056..85fc105d0e42 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1050,6 +1050,7 @@ jobs: 'node-express-esm-preload', 'node-express-esm-without-loader', 'node-express-cjs-preload', + 'node-otel-sdk-node', 'nextjs-app-dir', 'nextjs-14', 'nextjs-15', diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/.gitignore b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/.gitignore new file mode 100644 index 000000000000..1521c8b7652b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/.gitignore @@ -0,0 +1 @@ +dist diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/.npmrc b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/package.json b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/package.json new file mode 100644 index 000000000000..8a1634725184 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/package.json @@ -0,0 +1,32 @@ +{ + "name": "node-otel-sdk-trace", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "tsc", + "start": "node dist/app.js", + "test": "playwright test", + "clean": "npx rimraf node_modules pnpm-lock.yaml", + "test:build": "pnpm install && pnpm build", + "test:assert": "pnpm test" + }, + "dependencies": { + "@opentelemetry/sdk-node": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@sentry/core": "latest || *", + "@sentry/node": "latest || *", + "@sentry/opentelemetry": "latest || *", + "@sentry/types": "latest || *", + "@types/express": "4.17.17", + "@types/node": "18.15.1", + "express": "4.19.2", + "typescript": "4.9.5" + }, + "devDependencies": { + "@playwright/test": "^1.44.1", + "@sentry-internal/test-utils": "link:../../../test-utils" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/playwright.config.mjs new file mode 100644 index 000000000000..888e61cfb2dc --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/playwright.config.mjs @@ -0,0 +1,34 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig( + { + startCommand: `pnpm start`, + }, + { + webServer: [ + { + command: `node ./start-event-proxy.mjs`, + port: 3031, + stdout: 'pipe', + stderr: 'pipe', + }, + { + command: `node ./start-otel-proxy.mjs`, + port: 3032, + stdout: 'pipe', + stderr: 'pipe', + }, + { + command: 'pnpm start', + port: 3030, + stdout: 'pipe', + stderr: 'pipe', + env: { + PORT: 3030, + }, + }, + ], + }, +); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/src/app.ts b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/src/app.ts new file mode 100644 index 000000000000..26779990f6d1 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/src/app.ts @@ -0,0 +1,53 @@ +import './instrument'; + +// Other imports below +import * as Sentry from '@sentry/node'; +import express from 'express'; + +const app = express(); +const port = 3030; + +app.get('/test-success', function (req, res) { + res.send({ version: 'v1' }); +}); + +app.get('/test-param/:param', function (req, res) { + res.send({ paramWas: req.params.param }); +}); + +app.get('/test-transaction', function (req, res) { + Sentry.withActiveSpan(null, async () => { + Sentry.startSpan({ name: 'test-transaction', op: 'e2e-test' }, () => { + Sentry.startSpan({ name: 'test-span' }, () => undefined); + }); + + await Sentry.flush(); + + res.send({}); + }); +}); + +app.get('/test-error', async function (req, res) { + const exceptionId = Sentry.captureException(new Error('This is an error')); + + await Sentry.flush(2000); + + res.send({ exceptionId }); +}); + +app.get('/test-exception/:id', function (req, _res) { + throw new Error(`This is an exception with id ${req.params.id}`); +}); + +Sentry.setupExpressErrorHandler(app); + +app.use(function onError(err: unknown, req: any, res: any, next: any) { + // The error id is attached to `res.sentry` to be returned + // and optionally displayed to the user for support. + res.statusCode = 500; + res.end(res.sentry + '\n'); +}); + +app.listen(port, () => { + console.log(`Example app listening on port ${port}`); +}); diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/src/instrument.ts b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/src/instrument.ts new file mode 100644 index 000000000000..fb270e1252d3 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/src/instrument.ts @@ -0,0 +1,35 @@ +const opentelemetry = require('@opentelemetry/sdk-node'); +const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http'); +const Sentry = require('@sentry/node'); +const { SentrySpanProcessor, SentryPropagator, SentrySampler } = require('@sentry/opentelemetry'); + +const sentryClient = Sentry.init({ + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: + process.env.E2E_TEST_DSN || + 'https://3b6c388182fb435097f41d181be2b2ba@o4504321058471936.ingest.sentry.io/4504321066008576', + includeLocalVariables: true, + debug: !!process.env.DEBUG, + tunnel: `http://localhost:3031/`, // proxy server + tracesSampleRate: 1, + + skipOpenTelemetrySetup: true, +}); + +const sdk = new opentelemetry.NodeSDK({ + sampler: sentryClient ? new SentrySampler(sentryClient) : undefined, + textMapPropagator: new SentryPropagator(), + contextManager: new Sentry.SentryContextManager(), + spanProcessors: [ + new SentrySpanProcessor(), + new opentelemetry.node.BatchSpanProcessor( + new OTLPTraceExporter({ + url: 'http://localhost:3032/', + }), + ), + ], +}); + +sdk.start(); + +Sentry.validateOpenTelemetrySetup(); diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/start-event-proxy.mjs new file mode 100644 index 000000000000..8c74fa842a1b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'node-otel-sdk-trace', +}); diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/start-otel-proxy.mjs b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/start-otel-proxy.mjs new file mode 100644 index 000000000000..1cf9ef3e2c27 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/start-otel-proxy.mjs @@ -0,0 +1,6 @@ +import { startProxyServer } from '@sentry-internal/test-utils'; + +startProxyServer({ + port: 3032, + proxyServerName: 'node-otel-sdk-trace-otel', +}); diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tests/errors.test.ts new file mode 100644 index 000000000000..9cb97a051476 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tests/errors.test.ts @@ -0,0 +1,29 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; + +test('Sends correct error event', async ({ baseURL }) => { + const errorEventPromise = waitForError('node-otel-sdk-trace', event => { + return !event.type && event.exception?.values?.[0]?.value === 'This is an exception with id 123'; + }); + + await fetch(`${baseURL}/test-exception/123`); + + const errorEvent = await errorEventPromise; + + expect(errorEvent.exception?.values).toHaveLength(1); + expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123'); + + expect(errorEvent.request).toEqual({ + method: 'GET', + cookies: {}, + headers: expect.any(Object), + url: 'http://localhost:3030/test-exception/123', + }); + + expect(errorEvent.transaction).toEqual('GET /test-exception/:id'); + + expect(errorEvent.contexts?.trace).toEqual({ + trace_id: expect.any(String), + span_id: expect.any(String), + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tests/transactions.test.ts new file mode 100644 index 000000000000..39a7d27e9cb1 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tests/transactions.test.ts @@ -0,0 +1,213 @@ +import { expect, test } from '@playwright/test'; +import { waitForPlainRequest, waitForTransaction } from '@sentry-internal/test-utils'; + +test('Sends an API route transaction', async ({ baseURL }) => { + const pageloadTransactionEventPromise = waitForTransaction('node-otel-sdk-trace', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'http.server' && + transactionEvent?.transaction === 'GET /test-transaction' + ); + }); + + // Ensure we also send data to the OTLP endpoint + const otelPromise = waitForPlainRequest('node-otel-sdk-trace-otel', data => { + const json = JSON.parse(data) as any; + + return json.resourceSpans.length > 0; + }); + + await fetch(`${baseURL}/test-transaction`); + + const transactionEvent = await pageloadTransactionEventPromise; + + const otelData = await otelPromise; + + // For now we do not test the actual shape of this, but only existence + expect(otelData).toBeDefined(); + + expect(transactionEvent.contexts?.trace).toEqual({ + data: { + 'sentry.source': 'route', + 'sentry.origin': 'auto.http.otel.http', + 'sentry.op': 'http.server', + 'sentry.sample_rate': 1, + url: 'http://localhost:3030/test-transaction', + 'otel.kind': 'SERVER', + 'http.response.status_code': 200, + 'http.url': 'http://localhost:3030/test-transaction', + 'http.host': 'localhost:3030', + 'net.host.name': 'localhost', + 'http.method': 'GET', + 'http.scheme': 'http', + 'http.target': '/test-transaction', + 'http.user_agent': 'node', + 'http.flavor': '1.1', + 'net.transport': 'ip_tcp', + 'net.host.ip': expect.any(String), + 'net.host.port': expect.any(Number), + 'net.peer.ip': expect.any(String), + 'net.peer.port': expect.any(Number), + 'http.status_code': 200, + 'http.status_text': 'OK', + 'http.route': '/test-transaction', + }, + op: 'http.server', + span_id: expect.any(String), + status: 'ok', + trace_id: expect.any(String), + origin: 'auto.http.otel.http', + }); + + expect(transactionEvent).toEqual( + expect.objectContaining({ + transaction: 'GET /test-transaction', + type: 'transaction', + transaction_info: { + source: 'route', + }, + }), + ); + + const spans = transactionEvent.spans || []; + + expect(spans).toContainEqual({ + data: { + 'sentry.origin': 'auto.http.otel.express', + 'sentry.op': 'middleware.express', + 'http.route': '/', + 'express.name': 'query', + 'express.type': 'middleware', + 'otel.kind': 'INTERNAL', + }, + description: 'query', + op: 'middleware.express', + origin: 'auto.http.otel.express', + parent_span_id: expect.any(String), + span_id: expect.any(String), + start_timestamp: expect.any(Number), + status: 'ok', + timestamp: expect.any(Number), + trace_id: expect.any(String), + }); + + expect(spans).toContainEqual({ + data: { + 'sentry.origin': 'auto.http.otel.express', + 'sentry.op': 'middleware.express', + 'http.route': '/', + 'express.name': 'expressInit', + 'express.type': 'middleware', + 'otel.kind': 'INTERNAL', + }, + description: 'expressInit', + op: 'middleware.express', + origin: 'auto.http.otel.express', + parent_span_id: expect.any(String), + span_id: expect.any(String), + start_timestamp: expect.any(Number), + status: 'ok', + timestamp: expect.any(Number), + trace_id: expect.any(String), + }); + + expect(spans).toContainEqual({ + data: { + 'sentry.origin': 'auto.http.otel.express', + 'sentry.op': 'request_handler.express', + 'http.route': '/test-transaction', + 'express.name': '/test-transaction', + 'express.type': 'request_handler', + 'otel.kind': 'INTERNAL', + }, + description: '/test-transaction', + op: 'request_handler.express', + origin: 'auto.http.otel.express', + parent_span_id: expect.any(String), + span_id: expect.any(String), + start_timestamp: expect.any(Number), + status: 'ok', + timestamp: expect.any(Number), + trace_id: expect.any(String), + }); +}); + +test('Sends an API route transaction for an errored route', async ({ baseURL }) => { + const transactionEventPromise = waitForTransaction('node-otel-sdk-trace', transactionEvent => { + return ( + transactionEvent.contexts?.trace?.op === 'http.server' && + transactionEvent.transaction === 'GET /test-exception/:id' && + transactionEvent.request?.url === 'http://localhost:3030/test-exception/777' + ); + }); + + await fetch(`${baseURL}/test-exception/777`); + + const transactionEvent = await transactionEventPromise; + + expect(transactionEvent.contexts?.trace?.op).toEqual('http.server'); + expect(transactionEvent.transaction).toEqual('GET /test-exception/:id'); + expect(transactionEvent.contexts?.trace?.status).toEqual('internal_error'); + expect(transactionEvent.contexts?.trace?.data?.['http.status_code']).toEqual(500); + + const spans = transactionEvent.spans || []; + + expect(spans).toContainEqual({ + data: { + 'sentry.origin': 'auto.http.otel.express', + 'sentry.op': 'middleware.express', + 'http.route': '/', + 'express.name': 'query', + 'express.type': 'middleware', + 'otel.kind': 'INTERNAL', + }, + description: 'query', + op: 'middleware.express', + origin: 'auto.http.otel.express', + parent_span_id: expect.any(String), + span_id: expect.any(String), + start_timestamp: expect.any(Number), + status: 'ok', + timestamp: expect.any(Number), + trace_id: expect.any(String), + }); + + expect(spans).toContainEqual({ + data: { + 'sentry.origin': 'auto.http.otel.express', + 'sentry.op': 'middleware.express', + 'http.route': '/', + 'express.name': 'expressInit', + 'express.type': 'middleware', + 'otel.kind': 'INTERNAL', + }, + description: 'expressInit', + op: 'middleware.express', + origin: 'auto.http.otel.express', + parent_span_id: expect.any(String), + span_id: expect.any(String), + start_timestamp: expect.any(Number), + status: 'ok', + timestamp: expect.any(Number), + trace_id: expect.any(String), + }); + + expect(spans).toContainEqual({ + data: { + 'sentry.origin': 'auto.http.otel.express', + 'sentry.op': 'request_handler.express', + 'http.route': '/test-exception/:id', + 'express.name': '/test-exception/:id', + 'express.type': 'request_handler', + 'otel.kind': 'INTERNAL', + }, + description: '/test-exception/:id', + op: 'request_handler.express', + origin: 'auto.http.otel.express', + parent_span_id: expect.any(String), + span_id: expect.any(String), + start_timestamp: expect.any(Number), + status: 'ok', + timestamp: expect.any(Number), + trace_id: expect.any(String), + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tsconfig.json b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tsconfig.json new file mode 100644 index 000000000000..8cb64e989ed9 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-otel-sdk-node/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "types": ["node"], + "esModuleInterop": true, + "lib": ["es2018"], + "strict": true, + "outDir": "dist" + }, + "include": ["src/**/*.ts"] +} diff --git a/dev-packages/test-utils/src/event-proxy-server.ts b/dev-packages/test-utils/src/event-proxy-server.ts index 82693bf4af89..30bedadc38bb 100644 --- a/dev-packages/test-utils/src/event-proxy-server.ts +++ b/dev-packages/test-utils/src/event-proxy-server.ts @@ -1,6 +1,5 @@ import * as fs from 'fs'; import * as http from 'http'; -import * as https from 'https'; import type { AddressInfo } from 'net'; import * as os from 'os'; import * as path from 'path'; @@ -31,11 +30,27 @@ interface SentryRequestCallbackData { sentryResponseStatusCode?: number; } +type OnRequest = ( + eventCallbackListeners: Set<(data: string) => void>, + proxyRequest: http.IncomingMessage, + proxyRequestBody: string, +) => Promise<[number, string, Record | undefined]>; + /** - * Starts an event proxy server that will proxy events to sentry when the `tunnel` option is used. Point the `tunnel` - * option to this server (like this `tunnel: http://localhost:${port option}/`). + * Start a generic proxy server. + * The `onRequest` callback receives the incoming request and the request body, + * and should return a promise that resolves to a tuple with: + * statusCode, responseBody, responseHeaders */ -export async function startEventProxyServer(options: EventProxyServerOptions): Promise { +export async function startProxyServer( + options: { + /** Port to start the event proxy server at. */ + port: number; + /** The name for the proxy server used for referencing it with listener functions */ + proxyServerName: string; + }, + onRequest?: OnRequest, +): Promise { const eventCallbackListeners: Set<(data: string) => void> = new Set(); const proxyServer = http.createServer((proxyRequest, proxyResponse) => { @@ -59,102 +74,29 @@ export async function startEventProxyServer(options: EventProxyServerOptions): P ? zlib.gunzipSync(Buffer.concat(proxyRequestChunks)).toString() : Buffer.concat(proxyRequestChunks).toString(); - const envelopeHeader: EnvelopeItem[0] = JSON.parse(proxyRequestBody.split('\n')[0] as string); - - const shouldForwardEventToSentry = options.forwardToSentry != null ? options.forwardToSentry : true; - - if (!envelopeHeader.dsn && shouldForwardEventToSentry) { - // eslint-disable-next-line no-console - console.log( - '[event-proxy-server] Warn: No dsn on envelope header. Maybe a client-report was received. Proxy request body:', - proxyRequestBody, - ); - - proxyResponse.writeHead(200); - proxyResponse.write('{}', 'utf-8'); - proxyResponse.end(); - return; - } - - if (!shouldForwardEventToSentry) { - const data: SentryRequestCallbackData = { - envelope: parseEnvelope(proxyRequestBody), - rawProxyRequestBody: proxyRequestBody, - rawSentryResponseBody: '', - sentryResponseStatusCode: 200, - }; - eventCallbackListeners.forEach(listener => { - listener(Buffer.from(JSON.stringify(data)).toString('base64')); - }); - - proxyResponse.writeHead(200); - proxyResponse.write('{}', 'utf-8'); - proxyResponse.end(); - return; - } - - const { origin, pathname, host } = new URL(envelopeHeader.dsn as string); - - const projectId = pathname.substring(1); - const sentryIngestUrl = `${origin}/api/${projectId}/envelope/`; - - proxyRequest.headers.host = host; - - const sentryResponseChunks: Uint8Array[] = []; - - const sentryRequest = https.request( - sentryIngestUrl, - { headers: proxyRequest.headers, method: proxyRequest.method }, - sentryResponse => { - sentryResponse.addListener('data', (chunk: Buffer) => { - proxyResponse.write(chunk, 'binary'); - sentryResponseChunks.push(chunk); - }); - - sentryResponse.addListener('end', () => { - eventCallbackListeners.forEach(listener => { - const rawSentryResponseBody = Buffer.concat(sentryResponseChunks).toString(); - - try { - const data: SentryRequestCallbackData = { - envelope: parseEnvelope(proxyRequestBody), - rawProxyRequestBody: proxyRequestBody, - rawSentryResponseBody, - sentryResponseStatusCode: sentryResponse.statusCode, - }; - - listener(Buffer.from(JSON.stringify(data)).toString('base64')); - } catch (error) { - if (`${error}`.includes('Unexpected token') && proxyRequestBody.includes('{"type":"replay_event"}')) { - // eslint-disable-next-line no-console - console.log('[event-proxy-server] Info: Received replay event, skipping...'); - } else { - // eslint-disable-next-line no-console - console.error( - '[event-proxy-server] Error: Failed to parse Sentry request envelope', - error, - proxyRequestBody, - ); - } - } - }); - proxyResponse.end(); + const callback: OnRequest = + onRequest || + (async (eventCallbackListeners, proxyRequest, proxyRequestBody) => { + eventCallbackListeners.forEach(listener => { + listener(proxyRequestBody); }); - sentryResponse.addListener('error', err => { - // eslint-disable-next-line no-console - console.log('[event-proxy-server] Warn: Proxying to Sentry returned an error!', err); - proxyResponse.writeHead(500); - proxyResponse.write('{}', 'utf-8'); - proxyResponse.end(); - }); - - proxyResponse.writeHead(sentryResponse.statusCode || 500, sentryResponse.headers); - }, - ); + return [200, '{}', {}]; + }); - sentryRequest.write(Buffer.concat(proxyRequestChunks), 'binary'); - sentryRequest.end(); + callback(eventCallbackListeners, proxyRequest, proxyRequestBody) + .then(([statusCode, responseBody, responseHeaders]) => { + proxyResponse.writeHead(statusCode, responseHeaders); + proxyResponse.write(responseBody, 'utf-8'); + proxyResponse.end(); + }) + .catch(error => { + // eslint-disable-next-line no-console + console.log('[event-proxy-server] Warn: Proxy server returned an error', error); + proxyResponse.writeHead(500); + proxyResponse.write('{}', 'utf-8'); + proxyResponse.end(); + }); }); }); @@ -193,7 +135,113 @@ export async function startEventProxyServer(options: EventProxyServerOptions): P await eventCallbackServerStartupPromise; await proxyServerStartupPromise; - return; +} + +/** + * Starts an event proxy server that will proxy events to sentry when the `tunnel` option is used. Point the `tunnel` + * option to this server (like this `tunnel: http://localhost:${port option}/`). + */ +export async function startEventProxyServer(options: EventProxyServerOptions): Promise { + await startProxyServer(options, async (eventCallbackListeners, proxyRequest, proxyRequestBody) => { + const envelopeHeader: EnvelopeItem[0] = JSON.parse(proxyRequestBody.split('\n')[0] as string); + + const shouldForwardEventToSentry = options.forwardToSentry != null ? options.forwardToSentry : true; + + if (!envelopeHeader.dsn && shouldForwardEventToSentry) { + // eslint-disable-next-line no-console + console.log( + '[event-proxy-server] Warn: No dsn on envelope header. Maybe a client-report was received. Proxy request body:', + proxyRequestBody, + ); + + return [200, '{}', {}]; + } + + if (!shouldForwardEventToSentry) { + const data: SentryRequestCallbackData = { + envelope: parseEnvelope(proxyRequestBody), + rawProxyRequestBody: proxyRequestBody, + rawSentryResponseBody: '', + sentryResponseStatusCode: 200, + }; + eventCallbackListeners.forEach(listener => { + listener(Buffer.from(JSON.stringify(data)).toString('base64')); + }); + + return [200, '{}', {}]; + } + + const { origin, pathname, host } = new URL(envelopeHeader.dsn as string); + + const projectId = pathname.substring(1); + const sentryIngestUrl = `${origin}/api/${projectId}/envelope/`; + + proxyRequest.headers.host = host; + + const reqHeaders: Record = {}; + for (const [key, value] of Object.entries(proxyRequest.headers)) { + reqHeaders[key] = value as string; + } + + // Fetch does not like this + delete reqHeaders['transfer-encoding']; + + return fetch(sentryIngestUrl, { + body: proxyRequestBody, + headers: reqHeaders, + method: proxyRequest.method, + }).then(async res => { + const rawSentryResponseBody = await res.text(); + const data: SentryRequestCallbackData = { + envelope: parseEnvelope(proxyRequestBody), + rawProxyRequestBody: proxyRequestBody, + rawSentryResponseBody, + sentryResponseStatusCode: res.status, + }; + + eventCallbackListeners.forEach(listener => { + listener(Buffer.from(JSON.stringify(data)).toString('base64')); + }); + + const resHeaders: Record = {}; + for (const [key, value] of res.headers.entries()) { + resHeaders[key] = value; + } + + return [res.status, rawSentryResponseBody, resHeaders]; + }); + }); +} + +/** Wait for any plain request being made to the proxy. */ +export async function waitForPlainRequest( + proxyServerName: string, + callback: (eventData: string) => Promise | boolean, +): Promise { + const eventCallbackServerPort = await retrieveCallbackServerPort(proxyServerName); + + return new Promise((resolve, reject) => { + const request = http.request(`http://localhost:${eventCallbackServerPort}/`, {}, response => { + let eventContents = ''; + + response.on('error', err => { + reject(err); + }); + + response.on('data', (chunk: Buffer) => { + const chunkString = chunk.toString('utf8'); + + eventContents = eventContents.concat(chunkString); + + if (callback(eventContents)) { + response.destroy(); + return resolve(eventContents); + } + }); + }); + + request.end(); + }); } /** Wait for a request to be sent. */ diff --git a/dev-packages/test-utils/src/index.ts b/dev-packages/test-utils/src/index.ts index 4425d2688800..49685a6b18c2 100644 --- a/dev-packages/test-utils/src/index.ts +++ b/dev-packages/test-utils/src/index.ts @@ -1,9 +1,11 @@ export { + startProxyServer, startEventProxyServer, waitForEnvelopeItem, waitForError, waitForRequest, waitForTransaction, + waitForPlainRequest, } from './event-proxy-server'; export { getPlaywrightConfig } from './playwright-config'; From f60aae569eacf444014c19cf28177f74f2e0a657 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Mon, 1 Jul 2024 13:14:30 +0200 Subject: [PATCH 10/30] feat(nuxt): Configure sentry in external config (#12681) To be able to differentiate between a browser/client execution context, sentry is initialized in an external config file. An import statement in `nuxt-root.vue` is added which loads this config file. Nuxt tracking issue: https://github.com/getsentry/sentry-javascript/issues/9095 --------- Co-authored-by: Abhijeet Prasad --- packages/nuxt/README.md | 28 +++--- packages/nuxt/package.json | 38 ++++--- packages/nuxt/rollup.npm.config.mjs | 7 ++ packages/nuxt/src/client/sdk.ts | 8 +- packages/nuxt/src/common/debug-build.ts | 8 ++ packages/nuxt/src/common/snippets.ts | 47 +++++++++ packages/nuxt/src/common/types.ts | 3 +- packages/nuxt/src/index.types.ts | 2 +- packages/nuxt/src/module.ts | 38 +++++-- .../nuxt/src/runtime/plugins/sentry.client.ts | 15 +-- packages/nuxt/test/client/sdk.test.ts | 4 +- packages/nuxt/test/common/snippets.test.ts | 98 +++++++++++++++++++ 12 files changed, 253 insertions(+), 43 deletions(-) create mode 100644 packages/nuxt/rollup.npm.config.mjs create mode 100644 packages/nuxt/src/common/debug-build.ts create mode 100644 packages/nuxt/src/common/snippets.ts create mode 100644 packages/nuxt/test/common/snippets.test.ts diff --git a/packages/nuxt/README.md b/packages/nuxt/README.md index dcfc869cb108..e33201b21518 100644 --- a/packages/nuxt/README.md +++ b/packages/nuxt/README.md @@ -49,27 +49,33 @@ If the setup through the wizard doesn't work for you, you can also set up the SD yarn add @sentry/nuxt ``` -### 2. Client-side Setup +### 2. Nuxt Module Setup The Sentry Nuxt SDK is based on [Nuxt Modules](https://nuxt.com/docs/api/kit/modules). -1. Add `@sentry/nuxt` to the modules section of `nuxt.config.ts`: +1. Add `@sentry/nuxt/module` to the modules section of `nuxt.config.ts`: ```javascript // nuxt.config.ts export default defineNuxtConfig({ - modules: ['@sentry/nuxt'], - runtimeConfig: { - public: { - sentry: { - dsn: env.DSN, - // Additional config - }, - }, - }, + modules: ['@sentry/nuxt/module'], }); ``` +2. Add a `sentry.client.config.(js|ts)` file to the root of your project: + +```javascript +import * as Sentry from '@sentry/nuxt'; + +if (!import.meta.env.SSR) { + Sentry.init({ + dsn: env.DSN, + replaysSessionSampleRate: 0.1, + replaysOnErrorSampleRate: 1.0, + }); +} +``` + ### 3. Server-side Setup todo: add server-side setup diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 4862d08aba2d..1a9bbb4c009a 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -12,16 +12,28 @@ "files": [ "/build" ], - "main": "build/module.cjs", - "module": "build/module.mjs", - "types": "build/types.d.ts", + "main": "build/cjs/index.server.js", + "module": "build/esm/index.server.js", + "browser": "build/esm/index.client.js", + "types": "build/types/index.types.d.ts", "exports": { + "./package.json": "./package.json", ".": { - "types": "./build/types.d.ts", - "import": "./build/module.mjs", - "require": "./build/module.cjs" + "types": "./build/types/index.types.d.ts", + "browser": { + "import": "./build/esm/index.client.js", + "require": "./build/cjs/index.client.js" + }, + "node": { + "import": "./build/esm/index.server.js", + "require": "./build/cjs/index.server.js" + } }, - "./package.json": "./package.json" + "./module": { + "types": "./build/module/types.d.ts", + "import": "./build/module/module.mjs", + "require": "./build/module/module.cjs" + } }, "publishConfig": { "access": "public" @@ -31,6 +43,7 @@ }, "dependencies": { "@nuxt/kit": "^3.12.2", + "@sentry/browser": "8.13.0", "@sentry/core": "8.13.0", "@sentry/node": "8.13.0", "@sentry/opentelemetry": "8.13.0", @@ -44,12 +57,14 @@ "nuxt": "^3.12.2" }, "scripts": { - "build": "run-p build:transpile", + "build": "run-p build:transpile build:types build:nuxt-module", "build:dev": "yarn build", - "build:transpile": "nuxt-module-build build --outDir build", + "build:nuxt-module": "nuxt-module-build build --outDir build/module", + "build:transpile": "rollup -c rollup.npm.config.mjs", + "build:types": "tsc -p tsconfig.types.json", "build:watch": "run-p build:transpile:watch build:types:watch", "build:dev:watch": "yarn build:watch", - "build:transpile:watch": "nuxt-module-build build --outDir build --watch", + "build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:tarball": "npm pack", "circularDepCheck": "madge --circular src/index.client.ts && madge --circular src/index.server.ts && madge --circular src/index.types.ts", @@ -72,7 +87,8 @@ "^build:types" ], "outputs": [ - "{projectRoot}/build" + "{projectRoot}/build", + "{projectRoot}/build/module" ] } } diff --git a/packages/nuxt/rollup.npm.config.mjs b/packages/nuxt/rollup.npm.config.mjs new file mode 100644 index 000000000000..e800fdbba474 --- /dev/null +++ b/packages/nuxt/rollup.npm.config.mjs @@ -0,0 +1,7 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollup-utils'; + +export default makeNPMConfigVariants( + makeBaseNPMConfig({ + entrypoints: ['src/index.client.ts', 'src/client/index.ts'], + }), +); diff --git a/packages/nuxt/src/client/sdk.ts b/packages/nuxt/src/client/sdk.ts index 3a82dfb9f52f..e07c3267c902 100644 --- a/packages/nuxt/src/client/sdk.ts +++ b/packages/nuxt/src/client/sdk.ts @@ -1,19 +1,19 @@ +import { init as initBrowser } from '@sentry/browser'; import { applySdkMetadata } from '@sentry/core'; import type { Client } from '@sentry/types'; -import { init as initVue } from '@sentry/vue'; -import type { SentryVueOptions } from '../common/types'; +import type { SentryNuxtOptions } from '../common/types'; /** * Initializes the client-side of the Nuxt SDK * * @param options Configuration options for the SDK. */ -export function init(options: SentryVueOptions): Client | undefined { +export function init(options: SentryNuxtOptions): Client | undefined { const sentryOptions = { ...options, }; applySdkMetadata(sentryOptions, 'nuxt', ['nuxt', 'vue']); - return initVue(sentryOptions); + return initBrowser(sentryOptions); } diff --git a/packages/nuxt/src/common/debug-build.ts b/packages/nuxt/src/common/debug-build.ts new file mode 100644 index 000000000000..60aa50940582 --- /dev/null +++ b/packages/nuxt/src/common/debug-build.ts @@ -0,0 +1,8 @@ +declare const __DEBUG_BUILD__: boolean; + +/** + * This serves as a build time flag that will be true by default, but false in non-debug builds or if users replace `__SENTRY_DEBUG__` in their generated code. + * + * ATTENTION: This constant must never cross package boundaries (i.e. be exported) to guarantee that it can be used for tree shaking. + */ +export const DEBUG_BUILD = __DEBUG_BUILD__; diff --git a/packages/nuxt/src/common/snippets.ts b/packages/nuxt/src/common/snippets.ts new file mode 100644 index 000000000000..5b8a3f1f3ea1 --- /dev/null +++ b/packages/nuxt/src/common/snippets.ts @@ -0,0 +1,47 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +/** Returns an import snippet */ +export function buildSdkInitFileImportSnippet(filePath: string): string { + const posixPath = filePath.split(path.sep).join(path.posix.sep); + + // normalize to forward slashed for Windows-based systems + const normalizedPath = posixPath.replace(/\\/g, '/'); + + return `import '${normalizedPath}';`; +} + +/** + * Script tag inside `nuxt-root.vue` (root component we get from NuxtApp) + */ +export const SCRIPT_TAG = '