diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4f06228f3a7e..70535bd06e89 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1004,6 +1004,15 @@ jobs: timeout-minutes: 5 run: pnpm test:assert + - name: Upload Playwright Traces + uses: actions/upload-artifact@v4 + if: failure() + with: + name: playwright-traces-job_e2e_playwright_tests-${{ matrix.test-application}} + path: dev-packages/e2e-tests/test-applications/${{ matrix.test-application}}/test-results + overwrite: true + retention-days: 7 + 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/.size-limit.js b/.size-limit.js index d2211d91d5b0..5da293511976 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -10,6 +10,31 @@ module.exports = [ gzip: true, limit: '24 KB', }, + { + name: '@sentry/browser - with treeshaking flags', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init'), + gzip: true, + limit: '24 KB', + modifyWebpackConfig: function (config) { + const webpack = require('webpack'); + const TerserPlugin = require('terser-webpack-plugin'); + + config.plugins.push( + new webpack.DefinePlugin({ + __SENTRY_DEBUG__: false, + __RRWEB_EXCLUDE_SHADOW_DOM__: true, + __RRWEB_EXCLUDE_IFRAME__: true, + __SENTRY_EXCLUDE_REPLAY_WORKER__: true, + }), + ); + + config.optimization.minimize = true; + config.optimization.minimizer = [new TerserPlugin()]; + + return config; + }, + }, { name: '@sentry/browser (incl. Tracing)', path: 'packages/browser/build/npm/esm/index.js', @@ -32,6 +57,8 @@ module.exports = [ limit: '68 KB', modifyWebpackConfig: function (config) { const webpack = require('webpack'); + const TerserPlugin = require('terser-webpack-plugin'); + config.plugins.push( new webpack.DefinePlugin({ __SENTRY_DEBUG__: false, @@ -40,6 +67,10 @@ module.exports = [ __SENTRY_EXCLUDE_REPLAY_WORKER__: true, }), ); + + config.optimization.minimize = true; + config.optimization.minimizer = [new TerserPlugin()]; + return config; }, }, @@ -222,11 +253,17 @@ module.exports = [ ignore: [...builtinModules, ...nodePrefixedBuiltinModules], modifyWebpackConfig: function (config) { const webpack = require('webpack'); + const TerserPlugin = require('terser-webpack-plugin'); + config.plugins.push( new webpack.DefinePlugin({ __SENTRY_TRACING__: false, }), ); + + config.optimization.minimize = true; + config.optimization.minimizer = [new TerserPlugin()]; + return config; }, }, diff --git a/CHANGELOG.md b/CHANGELOG.md index 015c5370a75a..7b0f4361b92b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,57 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 8.29.0 + +### Important Changes + +- **Beta releases of official Solid and SolidStart Sentry SDKs** + +This release marks the beta releases of the `@sentry/solid` and `@sentry/solidstart` Sentry SDKs. For details on how to +use them, check out the +[Sentry Solid SDK README](https://github.com/getsentry/sentry-javascript/tree/develop/packages/solid) and the +[Sentry SolidStart SDK README](https://github.com/getsentry/sentry-javascript/tree/develop/packages/solidstart) +respectively. Please reach out on [GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have +any feedback or concerns. + +- **feat(node): Option to only wrap instrumented modules (#13139)** + +Adds the SDK option to only wrap ES modules with `import-in-the-middle` that specifically need to be instrumented. + +```javascript +import * as Sentry from '@sentry/node'; + +Sentry.init({ + dsn: '__PUBLIC_DSN__', + registerEsmLoaderHooks: { onlyHookedModules: true }, +}); +``` + +- **feat(node): Update OpenTelemetry packages to instrumentation v0.53.0 (#13587)** + +All internal OpenTelemetry instrumentation was updated to their latest version. This adds support for Mongoose v7 and v8 +and fixes various bugs related to ESM mode. + +### Other Changes + +- feat(nextjs): Emit warning when using turbopack (#13566) +- feat(nextjs): Future-proof Next.js config options overriding (#13586) +- feat(node): Add `generic-pool` integration (#13465) +- feat(nuxt): Upload sourcemaps generated by Nitro (#13382) +- feat(solidstart): Add `browserTracingIntegration` by default (#13561) +- feat(solidstart): Add `sentrySolidStartVite` plugin to simplify source maps upload (#13493) +- feat(vue): Only start UI spans if parent is available (#13568) +- fix(cloudflare): Guard `context.waitUntil` call in request handler (#13549) +- fix(gatsby): Fix assets path for sourcemaps upload (#13592) +- fix(nextjs): Use posix paths for sourcemap uploads (#13603) +- fix(node-fetch): Use stringified origin url (#13581) +- fix(node): Replace dashes in `generic-pool` span origins with underscores (#13579) +- fix(replay): Fix types in WebVitalData (#13573) +- fix(replay): Improve replay web vital types (#13602) +- fix(utils): Keep logger on carrier (#13570) + +Work in this release was contributed by @Zen-cronic. Thank you for your contribution! + ## 8.28.0 ### Important Changes diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index 886f91d328fb..d012484a4e0b 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -48,7 +48,7 @@ "babel-loader": "^8.2.2", "html-webpack-plugin": "^5.5.0", "pako": "^2.1.0", - "webpack": "^5.90.3" + "webpack": "^5.94.0" }, "devDependencies": { "@types/glob": "8.0.0", diff --git a/dev-packages/browser-integration-tests/suites/feedback/attachTo/init.js b/dev-packages/browser-integration-tests/suites/feedback/attachTo/init.js new file mode 100644 index 000000000000..5eb27143fdc7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/attachTo/init.js @@ -0,0 +1,17 @@ +import * as Sentry from '@sentry/browser'; +// Import this separately so that generatePlugin can handle it for CDN scenarios +import { feedbackIntegration } from '@sentry/browser'; + +const feedback = feedbackIntegration({ + autoInject: false, +}); + +window.Sentry = Sentry; +window.feedback = feedback; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [feedback], +}); + +feedback.attachTo('#custom-feedback-buttom'); diff --git a/dev-packages/browser-integration-tests/suites/feedback/attachTo/template.html b/dev-packages/browser-integration-tests/suites/feedback/attachTo/template.html new file mode 100644 index 000000000000..ae36b0c69c7b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/attachTo/template.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/feedback/attachTo/test.ts b/dev-packages/browser-integration-tests/suites/feedback/attachTo/test.ts new file mode 100644 index 000000000000..507b08685092 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/attachTo/test.ts @@ -0,0 +1,82 @@ +import { expect } from '@playwright/test'; + +import { TEST_HOST, sentryTest } from '../../../utils/fixtures'; +import { envelopeRequestParser, getEnvelopeType, shouldSkipFeedbackTest } from '../../../utils/helpers'; + +sentryTest('should capture feedback with custom button', async ({ getLocalTestUrl, page }) => { + if (shouldSkipFeedbackTest()) { + sentryTest.skip(); + } + + const feedbackRequestPromise = page.waitForResponse(res => { + const req = res.request(); + + const postData = req.postData(); + if (!postData) { + return false; + } + + try { + return getEnvelopeType(req) === 'feedback'; + } catch (err) { + return false; + } + }); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + await page.locator('#custom-feedback-buttom').click(); + await page.waitForSelector(':visible:text-is("Report a Bug")'); + + expect(await page.locator(':visible:text-is("Report a Bug")').count()).toEqual(1); + await page.locator('[name="name"]').fill('Jane Doe'); + await page.locator('[name="email"]').fill('janedoe@example.org'); + await page.locator('[name="message"]').fill('my example feedback'); + await page.locator('[data-sentry-feedback] .btn--primary').click(); + + const feedbackEvent = envelopeRequestParser((await feedbackRequestPromise).request()); + expect(feedbackEvent).toEqual({ + type: 'feedback', + breadcrumbs: expect.any(Array), + contexts: { + feedback: { + contact_email: 'janedoe@example.org', + message: 'my example feedback', + name: 'Jane Doe', + source: 'widget', + url: `${TEST_HOST}/index.html`, + }, + trace: { + trace_id: expect.stringMatching(/\w{32}/), + span_id: expect.stringMatching(/\w{16}/), + }, + }, + level: 'info', + timestamp: expect.any(Number), + event_id: expect.stringMatching(/\w{32}/), + environment: 'production', + tags: {}, + sdk: { + integrations: expect.arrayContaining(['Feedback']), + version: expect.any(String), + name: 'sentry.javascript.browser', + packages: expect.anything(), + }, + request: { + url: `${TEST_HOST}/index.html`, + headers: { + 'User-Agent': expect.stringContaining(''), + }, + }, + platform: 'javascript', + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/feedback/logger/init.js b/dev-packages/browser-integration-tests/suites/feedback/logger/init.js new file mode 100644 index 000000000000..3251bd6c7a4c --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/logger/init.js @@ -0,0 +1,19 @@ +import * as Sentry from '@sentry/browser'; +// Import this separately so that generatePlugin can handle it for CDN scenarios +import { feedbackIntegration } from '@sentry/browser'; + +const feedback = feedbackIntegration({ + autoInject: false, +}); + +window.Sentry = Sentry; +window.feedback = feedback; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + debug: true, + integrations: [feedback], +}); + +// This should log an error! +feedback.attachTo('#does-not-exist'); diff --git a/dev-packages/browser-integration-tests/suites/feedback/logger/test.ts b/dev-packages/browser-integration-tests/suites/feedback/logger/test.ts new file mode 100644 index 000000000000..34fadfc2503b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/logger/test.ts @@ -0,0 +1,36 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../utils/fixtures'; +import { shouldSkipFeedbackTest } from '../../../utils/helpers'; + +/** + * This test is mostly relevant for ensuring that the logger works in all combinations of CDN bundles. + * Even if feedback is included via the CDN, this test ensures that the logger is working correctly. + */ +sentryTest('should log error correctly', async ({ getLocalTestUrl, page }) => { + // In minified bundles we do not have logger messages, so we skip the test + if (shouldSkipFeedbackTest() || (process.env.PW_BUNDLE || '').includes('_min')) { + sentryTest.skip(); + } + + const messages: string[] = []; + + page.on('console', message => { + messages.push(message.text()); + }); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + + expect(messages).toContain('Sentry Logger [log]: Integration installed: Feedback'); + expect(messages).toContain('Sentry Logger [error]: [Feedback] Unable to attach to target element'); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpContext/init.js b/dev-packages/browser-integration-tests/suites/integrations/httpContext/init.js new file mode 100644 index 000000000000..984534454ac7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/httpContext/init.js @@ -0,0 +1,20 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +const integrations = Sentry.getDefaultIntegrations({}).filter( + defaultIntegration => defaultIntegration.name === 'HttpContext', +); + +const client = new Sentry.BrowserClient({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + transport: Sentry.makeFetchTransport, + stackParser: Sentry.defaultStackParser, + integrations: integrations, +}); + +const scope = new Sentry.Scope(); +scope.setClient(client); +client.init(); + +window._sentryScope = scope; diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpContext/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpContext/subject.js new file mode 100644 index 000000000000..62ce205e2ffc --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/httpContext/subject.js @@ -0,0 +1 @@ +window._sentryScope.captureException(new Error('client init')); diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts new file mode 100644 index 000000000000..02a62142e02b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts @@ -0,0 +1,26 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; + +sentryTest('httpContextIntegration captures user-agent and referrer', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const errorEventPromise = getFirstSentryEnvelopeRequest(page); + + // Simulate document.referrer being set to test full functionality of the integration + await page.goto(url, { referer: 'https://sentry.io/' }); + + const errorEvent = await errorEventPromise; + + expect(errorEvent.exception?.values).toHaveLength(1); + + expect(errorEvent.request).toEqual({ + headers: { + 'User-Agent': expect.any(String), + Referer: 'https://sentry.io/', + }, + url: expect.any(String), + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/init.js b/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/init.js deleted file mode 100644 index 8540ab176c38..000000000000 --- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/init.js +++ /dev/null @@ -1,11 +0,0 @@ -import * as Sentry from '@sentry/browser'; -import { httpClientIntegration } from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [httpClientIntegration()], - tracesSampleRate: 1, - sendDefaultPii: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/subject.js deleted file mode 100644 index 563b069e66cc..000000000000 --- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/subject.js +++ /dev/null @@ -1,8 +0,0 @@ -const xhr = new XMLHttpRequest(); - -xhr.open('GET', 'http://sentry-test.io/foo', true); -xhr.withCredentials = true; -xhr.setRequestHeader('Accept', 'application/json'); -xhr.setRequestHeader('Content-Type', 'application/json'); -xhr.setRequestHeader('Cache', 'no-cache'); -xhr.send(); diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/test.ts deleted file mode 100644 index f064a8652b48..000000000000 --- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { expect } from '@playwright/test'; -import type { Event } from '@sentry/types'; - -import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; - -sentryTest('works with httpClientIntegration', async ({ getLocalTestPath, page }) => { - const url = await getLocalTestPath({ testDir: __dirname }); - - await page.route('**/foo', route => { - return route.fulfill({ - status: 500, - body: JSON.stringify({ - error: { - message: 'Internal Server Error', - }, - }), - headers: { - 'Content-Type': 'text/html', - }, - }); - }); - - const eventData = await getFirstSentryEnvelopeRequest(page, url); - - expect(eventData.exception?.values).toHaveLength(1); - - // Not able to get the cookies from the request/response because of Playwright bug - // https://github.com/microsoft/playwright/issues/11035 - expect(eventData).toMatchObject({ - message: 'HTTP Client Error with status code: 500', - exception: { - values: [ - { - type: 'Error', - value: 'HTTP Client Error with status code: 500', - mechanism: { - type: 'http.client', - handled: false, - }, - }, - ], - }, - request: { - url: 'http://sentry-test.io/foo', - method: 'GET', - headers: { - accept: 'application/json', - cache: 'no-cache', - 'content-type': 'application/json', - }, - }, - contexts: { - response: { - status_code: 500, - body_size: 45, - headers: { - 'content-type': 'text/html', - 'content-length': '45', - }, - }, - }, - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/replay/logger/init.js b/dev-packages/browser-integration-tests/suites/replay/logger/init.js new file mode 100644 index 000000000000..195be16ddad3 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/replay/logger/init.js @@ -0,0 +1,18 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window.Replay = Sentry.replayIntegration({ + flushMinDelay: 200, + flushMaxDelay: 200, + minReplayDuration: 0, +}); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 0, + replaysSessionSampleRate: 1.0, + replaysOnErrorSampleRate: 0.0, + debug: true, + + integrations: [window.Replay], +}); diff --git a/dev-packages/browser-integration-tests/suites/replay/logger/test.ts b/dev-packages/browser-integration-tests/suites/replay/logger/test.ts new file mode 100644 index 000000000000..fa034a12b003 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/replay/logger/test.ts @@ -0,0 +1,36 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../utils/fixtures'; +import { shouldSkipReplayTest, waitForReplayRequest } from '../../../utils/replayHelpers'; + +sentryTest('should output logger messages', async ({ getLocalTestPath, page }) => { + // In minified bundles we do not have logger messages, so we skip the test + if (shouldSkipReplayTest() || (process.env.PW_BUNDLE || '').includes('_min')) { + sentryTest.skip(); + } + + const messages: string[] = []; + + page.on('console', message => { + messages.push(message.text()); + }); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const reqPromise0 = waitForReplayRequest(page, 0); + + const url = await getLocalTestPath({ testDir: __dirname }); + + await Promise.all([page.goto(url), reqPromise0]); + + expect(messages).toContain('Sentry Logger [log]: Integration installed: Replay'); + expect(messages).toContain('Sentry Logger [info]: [Replay] Creating new session'); + expect(messages).toContain('Sentry Logger [info]: [Replay] Starting replay in session mode'); + expect(messages).toContain('Sentry Logger [info]: [Replay] Using compression worker'); +}); diff --git a/dev-packages/browser-integration-tests/utils/generatePlugin.ts b/dev-packages/browser-integration-tests/utils/generatePlugin.ts index 9189ce63812f..acc583506df4 100644 --- a/dev-packages/browser-integration-tests/utils/generatePlugin.ts +++ b/dev-packages/browser-integration-tests/utils/generatePlugin.ts @@ -30,7 +30,6 @@ const useLoader = bundleKey.startsWith('loader'); const IMPORTED_INTEGRATION_CDN_BUNDLE_PATHS: Record = { httpClientIntegration: 'httpclient', captureConsoleIntegration: 'captureconsole', - CaptureConsole: 'captureconsole', debugIntegration: 'debug', rewriteFramesIntegration: 'rewriteframes', contextLinesIntegration: 'contextlines', diff --git a/dev-packages/bundle-analyzer-scenarios/package.json b/dev-packages/bundle-analyzer-scenarios/package.json index 6cec8aa83b2f..b1375418e16f 100644 --- a/dev-packages/bundle-analyzer-scenarios/package.json +++ b/dev-packages/bundle-analyzer-scenarios/package.json @@ -9,7 +9,7 @@ "private": true, "dependencies": { "html-webpack-plugin": "^5.6.0", - "webpack": "^5.92.1", + "webpack": "^5.94.0", "webpack-bundle-analyzer": "^4.10.2" }, "scripts": { diff --git a/dev-packages/e2e-tests/test-applications/node-express-esm-loader/src/instrument.mjs b/dev-packages/e2e-tests/test-applications/node-express-esm-loader/src/instrument.mjs index 544c773e5e7c..caaf73162ded 100644 --- a/dev-packages/e2e-tests/test-applications/node-express-esm-loader/src/instrument.mjs +++ b/dev-packages/e2e-tests/test-applications/node-express-esm-loader/src/instrument.mjs @@ -5,4 +5,5 @@ Sentry.init({ dsn: process.env.E2E_TEST_DSN, tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1, + registerEsmLoaderHooks: { onlyIncludeInstrumentedModules: true }, }); diff --git a/dev-packages/e2e-tests/test-applications/solidstart/app.config.ts b/dev-packages/e2e-tests/test-applications/solidstart/app.config.ts index b3c737efe5ba..0b9a5553fb0a 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/app.config.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart/app.config.ts @@ -1,3 +1,8 @@ +import { sentrySolidStartVite } from '@sentry/solidstart'; import { defineConfig } from '@solidjs/start/config'; -export default defineConfig({}); +export default defineConfig({ + vite: { + plugins: [sentrySolidStartVite()], + }, +}); diff --git a/dev-packages/e2e-tests/test-applications/solidstart/package.json b/dev-packages/e2e-tests/test-applications/solidstart/package.json index dfcf8a47402a..e831a14c1f47 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/package.json +++ b/dev-packages/e2e-tests/test-applications/solidstart/package.json @@ -3,14 +3,19 @@ "version": "0.0.0", "scripts": { "clean": "pnpx rimraf node_modules pnpm-lock.yaml .vinxi .output", + "clean:build": "pnpx rimraf .vinxi .output", "dev": "NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi dev", "build": "vinxi build", "//": [ "We are using `vinxi dev` to start the server because `vinxi start` is experimental and ", "doesn't correctly resolve modules for @sentry/solidstart/solidrouter.", - "This is currently not an issue outside of our repo. See: https://github.com/nksaraf/vinxi/issues/177" + "This is currently not an issue outside of our repo. See: https://github.com/nksaraf/vinxi/issues/177", + "We run the build command to ensure building succeeds. However, keeping", + "build output around slows down the vite dev server when using `@sentry/vite-plugin` so we clear it out", + "before actually running the tests.", + "Cleaning the build output should be removed once we can use `vinxi start`." ], - "preview": "HOST=localhost PORT=3030 NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi dev", + "preview": "pnpm clean:build && HOST=localhost PORT=3030 NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi dev", "start": "HOST=localhost PORT=3030 NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi start", "test:prod": "TEST_ENV=production playwright test", "test:build": "pnpm install && npx playwright install && pnpm build", diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/client-error.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/client-error.tsx index e997e4fbb1e3..5e405e8c4e40 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/client-error.tsx +++ b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/client-error.tsx @@ -1,75 +1,15 @@ -import * as Sentry from '@sentry/solidstart'; -import type { ParentProps } from 'solid-js'; -import { ErrorBoundary, createSignal, onMount } from 'solid-js'; - -const SentryErrorBoundary = Sentry.withSentryErrorBoundary(ErrorBoundary); - -const [count, setCount] = createSignal(1); -const [caughtError, setCaughtError] = createSignal(false); - export default function ClientErrorPage() { return ( - - {caughtError() && ( - - )} -
-
- -
-
- -
-
-
- ); -} - -function Throw(props: { error: string }) { - onMount(() => { - throw new Error(props.error); - }); - return null; -} - -function SampleErrorBoundary(props: ParentProps) { - return ( - ( -
-

Error Boundary Fallback

-
- {error.message} -
- -
- )} - > - {props.children} -
+
+ +
); } diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/error-boundary.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/error-boundary.tsx new file mode 100644 index 000000000000..b22607667e7e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/error-boundary.tsx @@ -0,0 +1,64 @@ +import * as Sentry from '@sentry/solidstart'; +import type { ParentProps } from 'solid-js'; +import { ErrorBoundary, createSignal, onMount } from 'solid-js'; + +const SentryErrorBoundary = Sentry.withSentryErrorBoundary(ErrorBoundary); + +const [count, setCount] = createSignal(1); +const [caughtError, setCaughtError] = createSignal(false); + +export default function ErrorBoundaryTestPage() { + return ( + + {caughtError() && ( + + )} +
+
+ +
+
+
+ ); +} + +function Throw(props: { error: string }) { + onMount(() => { + throw new Error(props.error); + }); + return null; +} + +function SampleErrorBoundary(props: ParentProps) { + return ( + ( +
+

Error Boundary Fallback

+
+ {error.message} +
+ +
+ )} + > + {props.children} +
+ ); +} diff --git a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx index eed722cba4e3..9a0b22cc38c6 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx +++ b/dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx @@ -14,6 +14,9 @@ export default function Home() {
  • Server error
  • +
  • + Error Boundary +
  • User 5 diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts index a4edf3c46236..b709760aab94 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts @@ -10,7 +10,7 @@ test('captures an exception', async ({ page }) => { ); }); - await page.goto('/client-error'); + await page.goto('/error-boundary'); await page.locator('#caughtErrorBtn').click(); const errorEvent = await errorEventPromise; @@ -27,7 +27,7 @@ test('captures an exception', async ({ page }) => { }, ], }, - transaction: '/client-error', + transaction: '/error-boundary', }); }); @@ -40,7 +40,7 @@ test('captures a second exception after resetting the boundary', async ({ page } ); }); - await page.goto('/client-error'); + await page.goto('/error-boundary'); await page.locator('#caughtErrorBtn').click(); const firstErrorEvent = await firstErrorEventPromise; @@ -57,7 +57,7 @@ test('captures a second exception after resetting the boundary', async ({ page } }, ], }, - transaction: '/client-error', + transaction: '/error-boundary', }); const secondErrorEventPromise = waitForError('solidstart', errorEvent => { @@ -85,6 +85,6 @@ test('captures a second exception after resetting the boundary', async ({ page } }, ], }, - transaction: '/client-error', + transaction: '/error-boundary', }); }); diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts index 0f5ef61b365a..e2b00786f54c 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts @@ -4,7 +4,7 @@ import { waitForError } from '@sentry-internal/test-utils'; test.describe('client-side errors', () => { test('captures error thrown on click', async ({ page }) => { const errorPromise = waitForError('solidstart', async errorEvent => { - return errorEvent?.exception?.values?.[0]?.value === 'Error thrown from Solid Start E2E test app'; + return errorEvent?.exception?.values?.[0]?.value === 'Uncaught error thrown from Solid Start E2E test app'; }); await page.goto(`/client-error`); @@ -16,9 +16,8 @@ test.describe('client-side errors', () => { values: [ { type: 'Error', - value: 'Error thrown from Solid Start E2E test app', + value: 'Uncaught error thrown from Solid Start E2E test app', mechanism: { - type: 'instrument', handled: false, }, }, diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index 9243a1512884..0ac7fdbf0aa2 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -44,6 +44,7 @@ "cors": "^2.8.5", "cron": "^3.1.6", "express": "^4.17.3", + "generic-pool": "^3.9.0", "graphql": "^16.3.0", "http-terminator": "^3.2.0", "ioredis": "^5.4.1", diff --git a/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/app.mjs b/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/app.mjs new file mode 100644 index 000000000000..6b20155aea38 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/app.mjs @@ -0,0 +1,21 @@ +import { loggingTransport } from '@sentry-internal/node-integration-tests'; +import * as Sentry from '@sentry/node'; +import * as iitm from 'import-in-the-middle'; + +new iitm.Hook((_, name) => { + if (name !== 'http') { + throw new Error(`'http' should be the only hooked modules but we just hooked '${name}'`); + } +}); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + autoSessionTracking: false, + transport: loggingTransport, + registerEsmLoaderHooks: { onlyIncludeInstrumentedModules: true }, +}); + +await import('./sub-module.mjs'); +await import('http'); +await import('os'); diff --git a/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/sub-module.mjs b/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/sub-module.mjs new file mode 100644 index 000000000000..9940c57857eb --- /dev/null +++ b/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/sub-module.mjs @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-console +console.assert(true); diff --git a/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/test.ts b/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/test.ts new file mode 100644 index 000000000000..8b9e6e06202f --- /dev/null +++ b/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/test.ts @@ -0,0 +1,12 @@ +import { conditionalTest } from '../../../utils'; +import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; + +afterAll(() => { + cleanupChildProcesses(); +}); + +conditionalTest({ min: 18 })('import-in-the-middle', () => { + test('onlyIncludeInstrumentedModules', done => { + createRunner(__dirname, 'app.mjs').ensureNoErrorOutput().start(done); + }); +}); diff --git a/dev-packages/node-integration-tests/suites/tracing/genericPool/scenario.js b/dev-packages/node-integration-tests/suites/tracing/genericPool/scenario.js new file mode 100644 index 000000000000..74d5f73693f5 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/genericPool/scenario.js @@ -0,0 +1,71 @@ +const { loggingTransport } = require('@sentry-internal/node-integration-tests'); +const Sentry = require('@sentry/node'); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + transport: loggingTransport, +}); + +// Stop the process from exiting before the transaction is sent +setInterval(() => {}, 1000); + +const mysql = require('mysql'); +const genericPool = require('generic-pool'); + +const factory = { + create: function () { + return mysql.createConnection({ + user: 'root', + password: 'docker', + }); + }, + destroy: function (client) { + client.end(err => { + if (err) { + // eslint-disable-next-line no-console + console.error('Error while disconnecting MySQL:', err); + } + }); + }, +}; + +const opts = { + max: 10, + min: 2, +}; + +const myPool = genericPool.createPool(factory, opts); + +async function run() { + await Sentry.startSpan( + { + op: 'transaction', + name: 'Test Transaction', + }, + async () => { + try { + const client1 = await myPool.acquire(); + const client2 = await myPool.acquire(); + + client1.query('SELECT NOW()', function () { + myPool.release(client1); + }); + + client2.query('SELECT 1 + 1 AS solution', function () { + myPool.release(client2); + }); + } catch (err) { + // eslint-disable-next-line no-console + console.error('Error while pooling MySQL:', err); + } finally { + await myPool.drain(); + await myPool.clear(); + } + }, + ); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts b/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts new file mode 100644 index 000000000000..114d3a909188 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts @@ -0,0 +1,34 @@ +import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; + +describe('genericPool auto instrumentation', () => { + afterAll(() => { + cleanupChildProcesses(); + }); + + test('should auto-instrument `genericPool` package when calling pool.require()', done => { + const EXPECTED_TRANSACTION = { + transaction: 'Test Transaction', + spans: expect.arrayContaining([ + expect.objectContaining({ + description: expect.stringMatching(/^generic-pool\.ac?quire/), + origin: 'auto.db.otel.generic_pool', + data: { + 'sentry.origin': 'auto.db.otel.generic_pool', + }, + status: 'ok', + }), + + expect.objectContaining({ + description: expect.stringMatching(/^generic-pool\.ac?quire/), + origin: 'auto.db.otel.generic_pool', + data: { + 'sentry.origin': 'auto.db.otel.generic_pool', + }, + status: 'ok', + }), + ]), + }; + + createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done); + }); +}); diff --git a/dev-packages/size-limit-gh-action/.eslintrc.cjs b/dev-packages/size-limit-gh-action/.eslintrc.cjs index 8c67e0037908..4a92730d3b0b 100644 --- a/dev-packages/size-limit-gh-action/.eslintrc.cjs +++ b/dev-packages/size-limit-gh-action/.eslintrc.cjs @@ -7,7 +7,7 @@ module.exports = { overrides: [ { - files: ['*.mjs'], + files: ['**/*.mjs'], extends: ['@sentry-internal/sdk/src/base'], }, ], diff --git a/dev-packages/size-limit-gh-action/index.mjs b/dev-packages/size-limit-gh-action/index.mjs index 3dbb8aa22127..848802256853 100644 --- a/dev-packages/size-limit-gh-action/index.mjs +++ b/dev-packages/size-limit-gh-action/index.mjs @@ -1,4 +1,3 @@ -/* eslint-disable max-lines */ import { promises as fs } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -9,13 +8,22 @@ import { exec } from '@actions/exec'; import { context, getOctokit } from '@actions/github'; import * as glob from '@actions/glob'; import * as io from '@actions/io'; -import bytes from 'bytes'; import { markdownTable } from 'markdown-table'; +import { SizeLimitFormatter } from './utils/SizeLimitFormatter.mjs'; +import { getArtifactsForBranchAndWorkflow } from './utils/getArtifactsForBranchAndWorkflow.mjs'; + const SIZE_LIMIT_HEADING = '## size-limit report 📦 '; const ARTIFACT_NAME = 'size-limit-action'; const RESULTS_FILE = 'size-limit-results.json'; +function getResultsFilePath() { + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + return path.resolve(__dirname, RESULTS_FILE); +} + +const { getInput, setFailed } = core; + async function fetchPreviousComment(octokit, repo, pr) { const { data: commentList } = await octokit.rest.issues.listComments({ ...repo, @@ -26,114 +34,6 @@ async function fetchPreviousComment(octokit, repo, pr) { return !sizeLimitComment ? null : sizeLimitComment; } -class SizeLimit { - formatBytes(size) { - return bytes.format(size, { unitSeparator: ' ' }); - } - - formatPercentageChange(base = 0, current = 0) { - if (base === 0) { - return 'added'; - } - - if (current === 0) { - return 'removed'; - } - - const value = ((current - base) / base) * 100; - const formatted = (Math.sign(value) * Math.ceil(Math.abs(value) * 100)) / 100; - - if (value > 0) { - return `+${formatted}%`; - } - - if (value === 0) { - return '-'; - } - - return `${formatted}%`; - } - - formatChange(base = 0, current = 0) { - if (base === 0) { - return 'added'; - } - - if (current === 0) { - return 'removed'; - } - - const value = current - base; - const formatted = this.formatBytes(value); - - if (value > 0) { - return `+${formatted} 🔺`; - } - - if (value === 0) { - return '-'; - } - - return `${formatted} 🔽`; - } - - formatLine(value, change) { - return `${value} (${change})`; - } - - formatSizeResult(name, base, current) { - return [ - name, - this.formatBytes(current.size), - this.formatPercentageChange(base.size, current.size), - this.formatChange(base.size, current.size), - ]; - } - - parseResults(output) { - const results = JSON.parse(output); - - return results.reduce((current, result) => { - return { - // biome-ignore lint/performance/noAccumulatingSpread: - ...current, - [result.name]: { - name: result.name, - size: +result.size, - }, - }; - }, {}); - } - - hasSizeChanges(base, current, threshold = 0) { - const names = [...new Set([...(base ? Object.keys(base) : []), ...Object.keys(current)])]; - - return !!names.find(name => { - const baseResult = base?.[name] || EmptyResult; - const currentResult = current[name] || EmptyResult; - - if (baseResult.size === 0 && currentResult.size === 0) { - return true; - } - - return Math.abs((currentResult.size - baseResult.size) / baseResult.size) * 100 > threshold; - }); - } - - formatResults(base, current) { - const names = [...new Set([...(base ? Object.keys(base) : []), ...Object.keys(current)])]; - const header = SIZE_RESULTS_HEADER; - const fields = names.map(name => { - const baseResult = base?.[name] || EmptyResult; - const currentResult = current[name] || EmptyResult; - - return this.formatSizeResult(name, baseResult, currentResult); - }); - - return [header, ...fields]; - } -} - async function execSizeLimit() { let output = ''; @@ -151,15 +51,8 @@ async function execSizeLimit() { return { status, output }; } -const SIZE_RESULTS_HEADER = ['Path', 'Size', '% Change', 'Change']; - -const EmptyResult = { - name: '-', - size: 0, -}; - async function run() { - const { getInput, setFailed } = core; + const __dirname = path.dirname(fileURLToPath(import.meta.url)); try { const { payload, repo } = context; @@ -174,36 +67,12 @@ async function run() { } const octokit = getOctokit(githubToken); - const limit = new SizeLimit(); - const artifactClient = artifact.create(); - const __dirname = path.dirname(fileURLToPath(import.meta.url)); - const resultsFilePath = path.resolve(__dirname, RESULTS_FILE); + const limit = new SizeLimitFormatter(); + const resultsFilePath = getResultsFilePath(); // If we have no comparison branch, we just run size limit & store the result as artifact if (!comparisonBranch) { - let base; - const { output: baseOutput } = await execSizeLimit(); - - try { - base = limit.parseResults(baseOutput); - } catch (error) { - core.error('Error parsing size-limit output. The output should be a json.'); - throw error; - } - - try { - await fs.writeFile(resultsFilePath, JSON.stringify(base), 'utf8'); - } catch (err) { - core.error(err); - } - const globber = await glob.create(resultsFilePath, { - followSymbolicLinks: false, - }); - const files = await globber.glob(); - - await artifactClient.uploadArtifact(ARTIFACT_NAME, files, __dirname); - - return; + return runSizeLimitOnComparisonBranch(); } // Else, we run size limit for the current branch, AND fetch it for the comparison branch @@ -213,12 +82,15 @@ async function run() { let baseWorkflowRun; try { + const workflowName = `${process.env.GITHUB_WORKFLOW || ''}`; + core.startGroup(`getArtifactsForBranchAndWorkflow - workflow:"${workflowName}", branch:"${comparisonBranch}"`); const artifacts = await getArtifactsForBranchAndWorkflow(octokit, { ...repo, artifactName: ARTIFACT_NAME, branch: comparisonBranch, - workflowName: `${process.env.GITHUB_WORKFLOW || ''}`, + workflowName, }); + core.endGroup(); if (!artifacts) { throw new Error('No artifacts found'); @@ -255,9 +127,12 @@ async function run() { const thresholdNumber = Number(threshold); - // @ts-ignore const sizeLimitComment = await fetchPreviousComment(octokit, repo, pr); + if (sizeLimitComment) { + core.debug('Found existing size limit comment, udpating it instead of creating a new one...'); + } + const shouldComment = isNaN(thresholdNumber) || limit.hasSizeChanges(base, current, thresholdNumber) || sizeLimitComment; @@ -269,8 +144,12 @@ async function run() { '⚠️ **Warning:** Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.', ); } - - bodyParts.push(markdownTable(limit.formatResults(base, current))); + try { + bodyParts.push(markdownTable(limit.formatResults(base, current))); + } catch (error) { + core.error('Error generating markdown table'); + core.error(error); + } if (baseWorkflowRun) { bodyParts.push(''); @@ -298,6 +177,8 @@ async function run() { "Error updating comment. This can happen for PR's originating from a fork without write permissions.", ); } + } else { + core.debug('Skipping comment because there are no changes.'); } if (status > 0) { @@ -309,136 +190,29 @@ async function run() { } } -// max pages of workflows to pagination through -const DEFAULT_MAX_PAGES = 50; -// max results per page -const DEFAULT_PAGE_LIMIT = 10; - -/** - * Fetch artifacts from a workflow run from a branch - * - * This is a bit hacky since GitHub Actions currently does not directly - * support downloading artifacts from other workflows - */ -/** - * Fetch artifacts from a workflow run from a branch - * - * This is a bit hacky since GitHub Actions currently does not directly - * support downloading artifacts from other workflows - */ -async function getArtifactsForBranchAndWorkflow(octokit, { owner, repo, workflowName, branch, artifactName }) { - core.startGroup(`getArtifactsForBranchAndWorkflow - workflow:"${workflowName}", branch:"${branch}"`); - - let repositoryWorkflow = null; +async function runSizeLimitOnComparisonBranch() { + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + const resultsFilePath = getResultsFilePath(); - // For debugging - const allWorkflows = []; - - // - // Find workflow id from `workflowName` - // - for await (const response of octokit.paginate.iterator(octokit.rest.actions.listRepoWorkflows, { - owner, - repo, - })) { - const targetWorkflow = response.data.find(({ name }) => name === workflowName); + const limit = new SizeLimitFormatter(); + const artifactClient = artifact.create(); - allWorkflows.push(...response.data.map(({ name }) => name)); + const { output: baseOutput } = await execSizeLimit(); - // If not found in responses, continue to search on next page - if (!targetWorkflow) { - continue; - } - - repositoryWorkflow = targetWorkflow; - break; - } - - if (!repositoryWorkflow) { - core.info( - `Unable to find workflow with name "${workflowName}" in the repository. Found workflows: ${allWorkflows.join( - ', ', - )}`, - ); - core.endGroup(); - return null; + try { + const base = limit.parseResults(baseOutput); + await fs.writeFile(resultsFilePath, JSON.stringify(base), 'utf8'); + } catch (error) { + core.error('Error parsing size-limit output. The output should be a json.'); + throw error; } - const workflow_id = repositoryWorkflow.id; - - let currentPage = 0; - let latestWorkflowRun = null; - - for await (const response of octokit.paginate.iterator(octokit.rest.actions.listWorkflowRuns, { - owner, - repo, - workflow_id, - branch, - per_page: DEFAULT_PAGE_LIMIT, - event: 'push', - })) { - if (!response.data.length) { - core.warning(`Workflow ${workflow_id} not found in branch ${branch}`); - core.endGroup(); - return null; - } - - // Do not allow downloading artifacts from a fork. - const filtered = response.data.filter(workflowRun => workflowRun.head_repository.full_name === `${owner}/${repo}`); - - // Sort to ensure the latest workflow run is the first - filtered.sort((a, b) => { - return new Date(b.created_at).getTime() - new Date(a.created_at).getTime(); - }); - - // Store the first workflow run, to determine if this is the latest one... - if (!latestWorkflowRun) { - latestWorkflowRun = filtered[0]; - } - - // Search through workflow artifacts until we find a workflow run w/ artifact name that we are looking for - for (const workflowRun of filtered) { - core.info(`Checking artifacts for workflow run: ${workflowRun.html_url}`); - - const { - data: { artifacts }, - } = await octokit.rest.actions.listWorkflowRunArtifacts({ - owner, - repo, - run_id: workflowRun.id, - }); - - if (!artifacts) { - core.warning( - `Unable to fetch artifacts for branch: ${branch}, workflow: ${workflow_id}, workflowRunId: ${workflowRun.id}`, - ); - } else { - const foundArtifact = artifacts.find(({ name }) => name === artifactName); - if (foundArtifact) { - core.info(`Found suitable artifact: ${foundArtifact.url}`); - return { - artifact: foundArtifact, - workflowRun, - isLatest: latestWorkflowRun.id === workflowRun.id, - }; - } else { - core.info(`No artifact found for ${artifactName}, trying next workflow run...`); - } - } - } - - if (currentPage > DEFAULT_MAX_PAGES) { - core.warning(`Workflow ${workflow_id} not found in branch: ${branch}`); - core.endGroup(); - return null; - } - - currentPage++; - } + const globber = await glob.create(resultsFilePath, { + followSymbolicLinks: false, + }); + const files = await globber.glob(); - core.warning(`Artifact not found: ${artifactName}`); - core.endGroup(); - return null; + await artifactClient.uploadArtifact(ARTIFACT_NAME, files, __dirname); } run(); diff --git a/dev-packages/size-limit-gh-action/utils/SizeLimitFormatter.mjs b/dev-packages/size-limit-gh-action/utils/SizeLimitFormatter.mjs new file mode 100644 index 000000000000..034281b38224 --- /dev/null +++ b/dev-packages/size-limit-gh-action/utils/SizeLimitFormatter.mjs @@ -0,0 +1,137 @@ +import * as core from '@actions/core'; +import bytes from 'bytes'; + +const SIZE_RESULTS_HEADER = ['Path', 'Size', '% Change', 'Change']; + +const EmptyResult = { + name: '-', + size: 0, +}; + +export class SizeLimitFormatter { + formatBytes(size) { + return bytes.format(size, { unitSeparator: ' ' }); + } + + formatName(name, sizeLimit, passed) { + if (passed) { + return name; + } + + return `⛔️ ${name} (max: ${this.formatBytes(sizeLimit)})`; + } + + formatPercentageChange(base = 0, current = 0) { + if (base === 0) { + return 'added'; + } + + if (current === 0) { + return 'removed'; + } + + const value = ((current - base) / base) * 100; + const formatted = (Math.sign(value) * Math.ceil(Math.abs(value) * 100)) / 100; + + if (value > 0) { + return `+${formatted}%`; + } + + if (value === 0) { + return '-'; + } + + return `${formatted}%`; + } + + formatChange(base = 0, current = 0) { + if (base === 0) { + return 'added'; + } + + if (current === 0) { + return 'removed'; + } + + const value = current - base; + const formatted = this.formatBytes(value); + + if (value > 0) { + return `+${formatted} 🔺`; + } + + if (value === 0) { + return '-'; + } + + return `${formatted} 🔽`; + } + + formatLine(value, change) { + return `${value} (${change})`; + } + + formatSizeResult(name, base, current) { + if (!current.passed) { + core.debug( + `Size limit exceeded for ${name} - ${this.formatBytes(current.size)} > ${this.formatBytes(current.sizeLimit)}`, + ); + } + + return [ + this.formatName(name, current.sizeLimit, current.passed), + this.formatBytes(current.size), + this.formatPercentageChange(base.size, current.size), + this.formatChange(base.size, current.size), + ]; + } + + parseResults(output) { + const results = JSON.parse(output); + + return results.reduce((current, result) => { + return { + // biome-ignore lint/performance/noAccumulatingSpread: + ...current, + [result.name]: { + name: result.name, + size: +result.size, + sizeLimit: +result.sizeLimit, + passed: result.passed || false, + }, + }; + }, {}); + } + + hasSizeChanges(base, current, threshold = 0) { + if (!base || !current) { + return true; + } + + const names = [...new Set([...Object.keys(base), ...Object.keys(current)])]; + + return names.some(name => { + const baseResult = base[name] || EmptyResult; + const currentResult = current[name] || EmptyResult; + + if (!baseResult.size || !currentResult.size) { + return true; + } + + return Math.abs((currentResult.size - baseResult.size) / baseResult.size) * 100 > threshold; + }); + } + + formatResults(base, current) { + const names = [...new Set([...(base ? Object.keys(base) : []), ...Object.keys(current)])]; + const header = SIZE_RESULTS_HEADER; + const fields = names.map(name => { + const baseResult = base?.[name] || EmptyResult; + const currentResult = current[name] || EmptyResult; + + return this.formatSizeResult(name, baseResult, currentResult); + }); + + return [header, ...fields]; + } +} diff --git a/dev-packages/size-limit-gh-action/utils/getArtifactsForBranchAndWorkflow.mjs b/dev-packages/size-limit-gh-action/utils/getArtifactsForBranchAndWorkflow.mjs new file mode 100644 index 000000000000..6d512b46afe1 --- /dev/null +++ b/dev-packages/size-limit-gh-action/utils/getArtifactsForBranchAndWorkflow.mjs @@ -0,0 +1,128 @@ +import * as core from '@actions/core'; + +// max pages of workflows to pagination through +const DEFAULT_MAX_PAGES = 50; +// max results per page +const DEFAULT_PAGE_LIMIT = 10; + +/** + * Fetch artifacts from a workflow run from a branch + * + * This is a bit hacky since GitHub Actions currently does not directly + * support downloading artifacts from other workflows + */ +/** + * Fetch artifacts from a workflow run from a branch + * + * This is a bit hacky since GitHub Actions currently does not directly + * support downloading artifacts from other workflows + */ +export async function getArtifactsForBranchAndWorkflow(octokit, { owner, repo, workflowName, branch, artifactName }) { + let repositoryWorkflow = null; + + // For debugging + const allWorkflows = []; + + // + // Find workflow id from `workflowName` + // + for await (const response of octokit.paginate.iterator(octokit.rest.actions.listRepoWorkflows, { + owner, + repo, + })) { + const targetWorkflow = response.data.find(({ name }) => name === workflowName); + + allWorkflows.push(...response.data.map(({ name }) => name)); + + // If not found in responses, continue to search on next page + if (!targetWorkflow) { + continue; + } + + repositoryWorkflow = targetWorkflow; + break; + } + + if (!repositoryWorkflow) { + core.info( + `Unable to find workflow with name "${workflowName}" in the repository. Found workflows: ${allWorkflows.join( + ', ', + )}`, + ); + return null; + } + + const workflow_id = repositoryWorkflow.id; + + let currentPage = 0; + let latestWorkflowRun = null; + + for await (const response of octokit.paginate.iterator(octokit.rest.actions.listWorkflowRuns, { + owner, + repo, + workflow_id, + branch, + per_page: DEFAULT_PAGE_LIMIT, + event: 'push', + })) { + if (!response.data.length) { + core.warning(`Workflow ${workflow_id} not found in branch ${branch}`); + return null; + } + + // Do not allow downloading artifacts from a fork. + const filtered = response.data.filter(workflowRun => workflowRun.head_repository.full_name === `${owner}/${repo}`); + + // Sort to ensure the latest workflow run is the first + filtered.sort((a, b) => { + return new Date(b.created_at).getTime() - new Date(a.created_at).getTime(); + }); + + // Store the first workflow run, to determine if this is the latest one... + if (!latestWorkflowRun) { + latestWorkflowRun = filtered[0]; + } + + // Search through workflow artifacts until we find a workflow run w/ artifact name that we are looking for + for (const workflowRun of filtered) { + core.info(`Checking artifacts for workflow run: ${workflowRun.html_url}`); + + const { + data: { artifacts }, + } = await octokit.rest.actions.listWorkflowRunArtifacts({ + owner, + repo, + run_id: workflowRun.id, + }); + + if (!artifacts) { + core.warning( + `Unable to fetch artifacts for branch: ${branch}, workflow: ${workflow_id}, workflowRunId: ${workflowRun.id}`, + ); + } else { + const foundArtifact = artifacts.find(({ name }) => name === artifactName); + if (foundArtifact) { + core.info(`Found suitable artifact: ${foundArtifact.url}`); + return { + artifact: foundArtifact, + workflowRun, + isLatest: latestWorkflowRun.id === workflowRun.id, + }; + } else { + core.info(`No artifact found for ${artifactName}, trying next workflow run...`); + } + } + } + + if (currentPage > DEFAULT_MAX_PAGES) { + core.warning(`Workflow ${workflow_id} not found in branch: ${branch}`); + return null; + } + + currentPage++; + } + + core.warning(`Artifact not found: ${artifactName}`); + core.endGroup(); + return null; +} diff --git a/dev-packages/test-utils/src/playwright-config.ts b/dev-packages/test-utils/src/playwright-config.ts index da2a10d0b477..d30c8cad4475 100644 --- a/dev-packages/test-utils/src/playwright-config.ts +++ b/dev-packages/test-utils/src/playwright-config.ts @@ -46,7 +46,7 @@ export function getPlaywrightConfig( baseURL: `http://localhost:${appPort}`, /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', + trace: 'retain-on-failure', }, /* Configure projects for major browsers */ diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index 0895eb86e364..747870da3014 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -43,6 +43,7 @@ export { fsIntegration, functionToStringIntegration, generateInstrumentOnce, + genericPoolIntegration, getActiveSpan, getAutoPerformanceIntegrations, getClient, diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index fa20bebe24ff..e6acf8e0e2f6 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -64,8 +64,8 @@ "access": "public" }, "dependencies": { - "@opentelemetry/instrumentation-aws-lambda": "0.43.0", - "@opentelemetry/instrumentation-aws-sdk": "0.43.1", + "@opentelemetry/instrumentation-aws-lambda": "0.44.0", + "@opentelemetry/instrumentation-aws-sdk": "0.44.0", "@sentry/core": "8.28.0", "@sentry/node": "8.28.0", "@sentry/types": "8.28.0", diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 27f48c2da0db..f648dba045ec 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -87,6 +87,7 @@ export { setupConnectErrorHandler, fastifyIntegration, fsIntegration, + genericPoolIntegration, graphqlIntegration, mongoIntegration, mongooseIntegration, diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index d4ca06f85584..1d8b02c33568 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -108,6 +108,7 @@ export { setupKoaErrorHandler, connectIntegration, setupConnectErrorHandler, + genericPoolIntegration, graphqlIntegration, mongoIntegration, mongooseIntegration, diff --git a/packages/cloudflare/src/request.ts b/packages/cloudflare/src/request.ts index 560c17afb9e7..7a474c3b27cb 100644 --- a/packages/cloudflare/src/request.ts +++ b/packages/cloudflare/src/request.ts @@ -31,7 +31,13 @@ export function wrapRequestHandler( handler: (...args: unknown[]) => Response | Promise, ): Promise { return withIsolationScope(async isolationScope => { - const { options, request, context } = wrapperOptions; + const { options, request } = wrapperOptions; + + // In certain situations, the passed context can become undefined. + // For example, for Astro while prerendering pages at build time. + // see: https://github.com/getsentry/sentry-javascript/issues/13217 + const context = wrapperOptions.context as ExecutionContext | undefined; + const client = init(options); isolationScope.setClient(client); @@ -89,7 +95,7 @@ export function wrapRequestHandler( captureException(e, { mechanism: { handled: false, type: 'cloudflare' } }); throw e; } finally { - context.waitUntil(flush(2000)); + context?.waitUntil(flush(2000)); } }, ); diff --git a/packages/cloudflare/test/request.test.ts b/packages/cloudflare/test/request.test.ts index 93764a292ab4..5218e8afe20b 100644 --- a/packages/cloudflare/test/request.test.ts +++ b/packages/cloudflare/test/request.test.ts @@ -45,6 +45,15 @@ describe('withSentry', () => { expect(context.waitUntil).toHaveBeenLastCalledWith(expect.any(Promise)); }); + test("doesn't error if context is undefined", () => { + expect(() => + wrapRequestHandler( + { options: MOCK_OPTIONS, request: new Request('https://example.com'), context: undefined as any }, + () => new Response('test'), + ), + ).not.toThrow(); + }); + test('creates a cloudflare client and sets it on the handler', async () => { const initAndBindSpy = vi.spyOn(SentryCore, 'initAndBind'); await wrapRequestHandler( diff --git a/packages/ember/package.json b/packages/ember/package.json index 2ff37964532e..1bec2d30f9bf 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -80,7 +80,7 @@ "qunit": "~2.19.2", "qunit-dom": "~2.0.0", "sinon": "15.2.0", - "webpack": "~5.90.3" + "webpack": "~5.94.0" }, "engines": { "node": ">=14.18" diff --git a/packages/gatsby/gatsby-node.js b/packages/gatsby/gatsby-node.js index d60914a03061..de88ee73adc0 100644 --- a/packages/gatsby/gatsby-node.js +++ b/packages/gatsby/gatsby-node.js @@ -12,7 +12,7 @@ exports.onCreateWebpackConfig = ({ getConfig, actions }, options) => { sentryWebpackPlugin({ sourcemaps: { // Only include files from the build output directory - assets: ['public'], + assets: ['./public/**'], // Ignore files that aren't users' source code related ignore: [ 'polyfill-*', // related to polyfills diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index f58305c64f6c..463a0c5c1246 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -87,6 +87,7 @@ export { connectIntegration, setupConnectErrorHandler, fastifyIntegration, + genericPoolIntegration, graphqlIntegration, mongoIntegration, mongooseIntegration, diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 6965ddcf5167..1dacc4435cd4 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -68,7 +68,7 @@ "access": "public" }, "dependencies": { - "@opentelemetry/instrumentation-http": "0.52.1", + "@opentelemetry/instrumentation-http": "0.53.0", "@opentelemetry/semantic-conventions": "^1.25.1", "@rollup/plugin-commonjs": "26.0.1", "@sentry/core": "8.28.0", @@ -91,7 +91,7 @@ }, "peerDependencies": { "next": "^13.2.0 || ^14.0 || ^15.0.0-rc.0", - "webpack": ">= 5.0.0" + "webpack": "5.94.0" }, "peerDependenciesMeta": { "webpack": { diff --git a/packages/nextjs/src/config/webpackPluginOptions.ts b/packages/nextjs/src/config/webpackPluginOptions.ts index 1bca9bff49b6..7b183047896a 100644 --- a/packages/nextjs/src/config/webpackPluginOptions.ts +++ b/packages/nextjs/src/config/webpackPluginOptions.ts @@ -11,37 +11,43 @@ export function getWebpackPluginOptions( buildContext: BuildContext, sentryBuildOptions: SentryBuildOptions, ): SentryWebpackPluginOptions { - const { buildId, isServer, config: userNextConfig, dir: projectDir, nextRuntime } = buildContext; + const { buildId, isServer, config: userNextConfig, dir, nextRuntime } = buildContext; const prefixInsert = !isServer ? 'Client' : nextRuntime === 'edge' ? 'Edge' : 'Node.js'; - const distDirAbsPath = path.join(projectDir, (userNextConfig as NextConfigObject).distDir || '.next'); // `.next` is the default directory + // We need to convert paths to posix because Glob patterns use `\` to escape + // glob characters. This clashes with Windows path separators. + // See: https://www.npmjs.com/package/glob + const projectDir = dir.replace(/\\/g, '/'); + // `.next` is the default directory + const distDir = (userNextConfig as NextConfigObject).distDir?.replace(/\\/g, '/') ?? '.next'; + const distDirAbsPath = path.posix.join(projectDir, distDir); let sourcemapUploadAssets: string[] = []; const sourcemapUploadIgnore: string[] = []; if (isServer) { sourcemapUploadAssets.push( - path.join(distDirAbsPath, 'server', '**'), // This is normally where Next.js outputs things - path.join(distDirAbsPath, 'serverless', '**'), // This was the output location for serverless Next.js + path.posix.join(distDirAbsPath, 'server', '**'), // This is normally where Next.js outputs things + path.posix.join(distDirAbsPath, 'serverless', '**'), // This was the output location for serverless Next.js ); } else { if (sentryBuildOptions.widenClientFileUpload) { - sourcemapUploadAssets.push(path.join(distDirAbsPath, 'static', 'chunks', '**')); + sourcemapUploadAssets.push(path.posix.join(distDirAbsPath, 'static', 'chunks', '**')); } else { sourcemapUploadAssets.push( - path.join(distDirAbsPath, 'static', 'chunks', 'pages', '**'), - path.join(distDirAbsPath, 'static', 'chunks', 'app', '**'), + path.posix.join(distDirAbsPath, 'static', 'chunks', 'pages', '**'), + path.posix.join(distDirAbsPath, 'static', 'chunks', 'app', '**'), ); } // TODO: We should think about uploading these when `widenClientFileUpload` is `true`. They may be useful in some situations. sourcemapUploadIgnore.push( - path.join(distDirAbsPath, 'static', 'chunks', 'framework-*'), - path.join(distDirAbsPath, 'static', 'chunks', 'framework.*'), - path.join(distDirAbsPath, 'static', 'chunks', 'main-*'), - path.join(distDirAbsPath, 'static', 'chunks', 'polyfills-*'), - path.join(distDirAbsPath, 'static', 'chunks', 'webpack-*'), + path.posix.join(distDirAbsPath, 'static', 'chunks', 'framework-*'), + path.posix.join(distDirAbsPath, 'static', 'chunks', 'framework.*'), + path.posix.join(distDirAbsPath, 'static', 'chunks', 'main-*'), + path.posix.join(distDirAbsPath, 'static', 'chunks', 'polyfills-*'), + path.posix.join(distDirAbsPath, 'static', 'chunks', 'webpack-*'), ); } @@ -79,9 +85,9 @@ export function getWebpackPluginOptions( // We only care to delete client bundle source maps because they would be the ones being served. // Removing the server source maps crashes Vercel builds for (thus far) unknown reasons: // https://github.com/getsentry/sentry-javascript/issues/13099 - path.join(distDirAbsPath, 'static', '**', '*.js.map'), - path.join(distDirAbsPath, 'static', '**', '*.mjs.map'), - path.join(distDirAbsPath, 'static', '**', '*.cjs.map'), + path.posix.join(distDirAbsPath, 'static', '**', '*.js.map'), + path.posix.join(distDirAbsPath, 'static', '**', '*.mjs.map'), + path.posix.join(distDirAbsPath, 'static', '**', '*.cjs.map'), ] : undefined, ...sentryBuildOptions.unstable_sentryWebpackPluginOptions?.sourcemaps, diff --git a/packages/nextjs/src/config/withSentryConfig.ts b/packages/nextjs/src/config/withSentryConfig.ts index 78df524c88c0..4f5205fecfcb 100644 --- a/packages/nextjs/src/config/withSentryConfig.ts +++ b/packages/nextjs/src/config/withSentryConfig.ts @@ -1,3 +1,4 @@ +/* eslint-disable complexity */ import { isThenable, parseSemver } from '@sentry/utils'; import * as fs from 'fs'; @@ -47,6 +48,7 @@ function getFinalConfigObject( incomingUserNextConfigObject: NextConfigObject, userSentryOptions: SentryBuildOptions, ): NextConfigObject { + // TODO(v9): Remove this check for the Sentry property if ('sentry' in incomingUserNextConfigObject) { // eslint-disable-next-line no-console console.warn( @@ -71,21 +73,10 @@ function getFinalConfigObject( } } - // We need to enable `instrumentation.ts` for users because we tell them to put their `Sentry.init()` calls inside of it. - if (incomingUserNextConfigObject.experimental?.instrumentationHook === false) { - // eslint-disable-next-line no-console - console.warn( - '[@sentry/nextjs] You turned off the `instrumentationHook` option. Note that Sentry will not be initialized if you did not set it up inside `instrumentation.ts`.', - ); - } - incomingUserNextConfigObject.experimental = { - instrumentationHook: true, - ...incomingUserNextConfigObject.experimental, - }; + const nextJsVersion = getNextjsVersion(); // Add the `clientTraceMetadata` experimental option based on Next.js version. The option got introduced in Next.js version 15.0.0 (actually 14.3.0-canary.64). // Adding the option on lower versions will cause Next.js to print nasty warnings we wouldn't confront our users with. - const nextJsVersion = getNextjsVersion(); if (nextJsVersion) { const { major, minor } = parseSemver(nextJsVersion); if (major !== undefined && minor !== undefined && (major >= 15 || (major === 14 && minor >= 3))) { @@ -99,7 +90,84 @@ function getFinalConfigObject( } else { // eslint-disable-next-line no-console console.log( - "[@sentry/nextjs] The Sentry SDK was not able to determine your Next.js version. If you are using Next.js 15 or greater, please add `experimental.clientTraceMetadata: ['sentry-trace', 'baggage']` to your Next.js config to enable pageload tracing for App Router.", + "[@sentry/nextjs] The Sentry SDK was not able to determine your Next.js version. If you are using Next.js version 15 or greater, please add `experimental.clientTraceMetadata: ['sentry-trace', 'baggage']` to your Next.js config to enable pageload tracing for App Router.", + ); + } + + // From Next.js version (15.0.0-canary.124) onwards, Next.js does no longer require the `experimental.instrumentationHook` option and will + // print a warning when it is set, so we need to conditionally provide it for lower versions. + if (nextJsVersion) { + const { major, minor, patch, prerelease } = parseSemver(nextJsVersion); + const isFullySupportedRelease = + major !== undefined && + minor !== undefined && + patch !== undefined && + major >= 15 && + ((minor === 0 && patch === 0 && prerelease === undefined) || minor > 0 || patch > 0); + const isSupportedV15Rc = + major !== undefined && + minor !== undefined && + patch !== undefined && + prerelease !== undefined && + major === 15 && + minor === 0 && + patch === 0 && + prerelease.startsWith('rc.') && + parseInt(prerelease.split('.')[1] || '', 10) > 0; + const isSupportedCanary = + minor !== undefined && + patch !== undefined && + prerelease !== undefined && + major === 15 && + minor === 0 && + patch === 0 && + prerelease.startsWith('canary.') && + parseInt(prerelease.split('.')[1] || '', 10) >= 124; + + if (!isFullySupportedRelease && !isSupportedV15Rc && !isSupportedCanary) { + if (incomingUserNextConfigObject.experimental?.instrumentationHook === false) { + // eslint-disable-next-line no-console + console.warn( + '[@sentry/nextjs] You turned off the `experimental.instrumentationHook` option. Note that Sentry will not be initialized if you did not set it up inside `instrumentation.(js|ts)`.', + ); + } + incomingUserNextConfigObject.experimental = { + instrumentationHook: true, + ...incomingUserNextConfigObject.experimental, + }; + } + } else { + // If we cannot detect a Next.js version for whatever reason, the sensible default is to set the `experimental.instrumentationHook`, even though it may create a warning. + if ( + incomingUserNextConfigObject.experimental && + 'instrumentationHook' in incomingUserNextConfigObject.experimental + ) { + if (incomingUserNextConfigObject.experimental.instrumentationHook === false) { + // eslint-disable-next-line no-console + console.warn( + '[@sentry/nextjs] You set `experimental.instrumentationHook` to `false`. If you are using Next.js version 15 or greater, you can remove that option. If you are using Next.js version 14 or lower, you need to set `experimental.instrumentationHook` in your `next.config.(js|mjs)` to `true` for the SDK to be properly initialized in combination with `instrumentation.(js|ts)`.', + ); + } + } else { + // eslint-disable-next-line no-console + console.log( + "[@sentry/nextjs] The Sentry SDK was not able to determine your Next.js version. If you are using Next.js version 15 or greater, Next.js will probably show you a warning about the `experimental.instrumentationHook` being set. To silence Next.js' warning, explicitly set the `experimental.instrumentationHook` option in your `next.config.(js|mjs|ts)` to `undefined`. If you are on Next.js version 14 or lower, you can silence this particular warning by explicitly setting the `experimental.instrumentationHook` option in your `next.config.(js|mjs)` to `true`.", + ); + incomingUserNextConfigObject.experimental = { + instrumentationHook: true, + ...incomingUserNextConfigObject.experimental, + }; + } + } + + if (process.env.TURBOPACK && !process.env.SENTRY_SUPPRESS_TURBOPACK_WARNING) { + // eslint-disable-next-line no-console + console.warn( + `[@sentry/nextjs] WARNING: You are using the Sentry SDK with \`next ${ + process.env.NODE_ENV === 'development' ? 'dev' : 'build' + } --turbo\`. The Sentry SDK doesn't yet fully support Turbopack. The SDK will not be loaded in the browser, and serverside instrumentation will be inaccurate or incomplete. ${ + process.env.NODE_ENV === 'development' ? 'Production builds without `--turbo` will still fully work. ' : '' + }If you are just trying out Sentry or attempting to configure the SDK, we recommend temporarily removing the \`--turbo\` flag while you are developing locally. Follow this issue for progress on Sentry + Turbopack: https://github.com/getsentry/sentry-javascript/issues/8105. (You can suppress this warning by setting SENTRY_SUPPRESS_TURBOPACK_WARNING=1 as environment variable)`, ); } diff --git a/packages/nextjs/test/config/webpack/webpackPluginOptions.test.ts b/packages/nextjs/test/config/webpack/webpackPluginOptions.test.ts index 557859b2a7e1..177077d2b5c4 100644 --- a/packages/nextjs/test/config/webpack/webpackPluginOptions.test.ts +++ b/packages/nextjs/test/config/webpack/webpackPluginOptions.test.ts @@ -2,6 +2,7 @@ import type { BuildContext, NextConfigObject } from '../../../src/config/types'; import { getWebpackPluginOptions } from '../../../src/config/webpackPluginOptions'; function generateBuildContext(overrides: { + dir?: string; isServer: boolean; nextjsConfig?: NextConfigObject; }): BuildContext { @@ -9,7 +10,7 @@ function generateBuildContext(overrides: { dev: false, // The plugin is not included in dev mode isServer: overrides.isServer, buildId: 'test-build-id', - dir: '/my/project/dir', + dir: overrides.dir ?? '/my/project/dir', config: overrides.nextjsConfig ?? {}, totalPages: 2, defaultLoaders: true, @@ -171,4 +172,23 @@ describe('getWebpackPluginOptions()', () => { assets: [], }); }); + + it('passes posix paths to the plugin', () => { + const buildContext = generateBuildContext({ + dir: 'C:\\my\\windows\\project\\dir', + nextjsConfig: { distDir: '.dist\\v1' }, + isServer: false, + }); + const generatedPluginOptions = getWebpackPluginOptions(buildContext, { widenClientFileUpload: true }); + expect(generatedPluginOptions.sourcemaps).toMatchObject({ + assets: ['C:/my/windows/project/dir/.dist/v1/static/chunks/**'], + ignore: [ + 'C:/my/windows/project/dir/.dist/v1/static/chunks/framework-*', + 'C:/my/windows/project/dir/.dist/v1/static/chunks/framework.*', + 'C:/my/windows/project/dir/.dist/v1/static/chunks/main-*', + 'C:/my/windows/project/dir/.dist/v1/static/chunks/polyfills-*', + 'C:/my/windows/project/dir/.dist/v1/static/chunks/webpack-*', + ], + }); + }); }); diff --git a/packages/node/package.json b/packages/node/package.json index f17c37541f63..9a7091ef57fc 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -68,27 +68,28 @@ "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.1", "@opentelemetry/core": "^1.25.1", - "@opentelemetry/instrumentation": "^0.52.1", - "@opentelemetry/instrumentation-connect": "0.38.0", - "@opentelemetry/instrumentation-express": "0.41.1", - "@opentelemetry/instrumentation-fastify": "0.38.0", - "@opentelemetry/instrumentation-fs": "0.14.0", - "@opentelemetry/instrumentation-graphql": "0.42.0", - "@opentelemetry/instrumentation-hapi": "0.40.0", - "@opentelemetry/instrumentation-http": "0.52.1", - "@opentelemetry/instrumentation-ioredis": "0.42.0", - "@opentelemetry/instrumentation-koa": "0.42.0", - "@opentelemetry/instrumentation-mongodb": "0.46.0", - "@opentelemetry/instrumentation-mongoose": "0.40.0", - "@opentelemetry/instrumentation-mysql": "0.40.0", - "@opentelemetry/instrumentation-mysql2": "0.40.0", - "@opentelemetry/instrumentation-nestjs-core": "0.39.0", - "@opentelemetry/instrumentation-pg": "0.43.0", - "@opentelemetry/instrumentation-redis-4": "0.41.0", + "@opentelemetry/instrumentation": "^0.53.0", + "@opentelemetry/instrumentation-connect": "0.39.0", + "@opentelemetry/instrumentation-express": "0.42.0", + "@opentelemetry/instrumentation-fastify": "0.39.0", + "@opentelemetry/instrumentation-fs": "0.15.0", + "@opentelemetry/instrumentation-generic-pool": "0.39.0", + "@opentelemetry/instrumentation-graphql": "0.43.0", + "@opentelemetry/instrumentation-hapi": "0.41.0", + "@opentelemetry/instrumentation-http": "0.53.0", + "@opentelemetry/instrumentation-ioredis": "0.43.0", + "@opentelemetry/instrumentation-koa": "0.43.0", + "@opentelemetry/instrumentation-mongodb": "0.47.0", + "@opentelemetry/instrumentation-mongoose": "0.42.0", + "@opentelemetry/instrumentation-mysql": "0.41.0", + "@opentelemetry/instrumentation-mysql2": "0.41.0", + "@opentelemetry/instrumentation-nestjs-core": "0.40.0", + "@opentelemetry/instrumentation-pg": "0.44.0", + "@opentelemetry/instrumentation-redis-4": "0.42.0", "@opentelemetry/resources": "^1.25.1", "@opentelemetry/sdk-trace-base": "^1.25.1", "@opentelemetry/semantic-conventions": "^1.25.1", - "@prisma/instrumentation": "5.18.0", + "@prisma/instrumentation": "5.19.1", "@sentry/core": "8.28.0", "@sentry/opentelemetry": "8.28.0", "@sentry/types": "8.28.0", diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 5f4adf315fd5..6ce3c325e3ff 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -26,6 +26,7 @@ export { hapiIntegration, setupHapiErrorHandler } from './integrations/tracing/h export { koaIntegration, setupKoaErrorHandler } from './integrations/tracing/koa'; export { connectIntegration, setupConnectErrorHandler } from './integrations/tracing/connect'; export { spotlightIntegration } from './integrations/spotlight'; +export { genericPoolIntegration } from './integrations/tracing/genericPool'; export { SentryContextManager } from './otel/contextManager'; export { generateInstrumentOnce } from './otel/instrument'; diff --git a/packages/node/src/integrations/node-fetch.ts b/packages/node/src/integrations/node-fetch.ts index 093b314a6138..fa7a9974135a 100644 --- a/packages/node/src/integrations/node-fetch.ts +++ b/packages/node/src/integrations/node-fetch.ts @@ -192,11 +192,11 @@ function getBreadcrumbData(request: FetchRequest): Partial function getAbsoluteUrl(origin: string, path: string = '/'): string { const url = `${origin}`; - if (origin.endsWith('/') && path.startsWith('/')) { + if (url.endsWith('/') && path.startsWith('/')) { return `${url}${path.slice(1)}`; } - if (!origin.endsWith('/') && !path.startsWith('/')) { + if (!url.endsWith('/') && !path.startsWith('/')) { return `${url}/${path.slice(1)}`; } diff --git a/packages/node/src/integrations/tracing/genericPool.ts b/packages/node/src/integrations/tracing/genericPool.ts new file mode 100644 index 000000000000..8bc84554071c --- /dev/null +++ b/packages/node/src/integrations/tracing/genericPool.ts @@ -0,0 +1,40 @@ +import { GenericPoolInstrumentation } from '@opentelemetry/instrumentation-generic-pool'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, spanToJSON } from '@sentry/core'; +import type { IntegrationFn } from '@sentry/types'; +import { generateInstrumentOnce } from '../../otel/instrument'; + +const INTEGRATION_NAME = 'GenericPool'; + +export const instrumentGenericPool = generateInstrumentOnce(INTEGRATION_NAME, () => new GenericPoolInstrumentation({})); + +const _genericPoolIntegration = (() => { + return { + name: INTEGRATION_NAME, + setupOnce() { + instrumentGenericPool(); + }, + + setup(client) { + client.on('spanStart', span => { + const spanJSON = spanToJSON(span); + + const spanDescription = spanJSON.description; + + // typo in emitted span for version <= 0.38.0 of @opentelemetry/instrumentation-generic-pool + const isGenericPoolSpan = + spanDescription === 'generic-pool.aquire' || spanDescription === 'generic-pool.acquire'; + + if (isGenericPoolSpan) { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.generic_pool'); + } + }); + }, + }; +}) satisfies IntegrationFn; + +/** + * GenericPool integration + * + * Capture tracing data for GenericPool. + */ +export const genericPoolIntegration = defineIntegration(_genericPoolIntegration); diff --git a/packages/node/src/integrations/tracing/index.ts b/packages/node/src/integrations/tracing/index.ts index 886c11683674..46a9f79e4caa 100644 --- a/packages/node/src/integrations/tracing/index.ts +++ b/packages/node/src/integrations/tracing/index.ts @@ -4,6 +4,7 @@ import { instrumentHttp } from '../http'; import { connectIntegration, instrumentConnect } from './connect'; import { expressIntegration, instrumentExpress } from './express'; import { fastifyIntegration, instrumentFastify } from './fastify'; +import { genericPoolIntegration, instrumentGenericPool } from './genericPool'; import { graphqlIntegration, instrumentGraphql } from './graphql'; import { hapiIntegration, instrumentHapi } from './hapi'; import { instrumentKoa, koaIntegration } from './koa'; @@ -37,6 +38,7 @@ export function getAutoPerformanceIntegrations(): Integration[] { hapiIntegration(), koaIntegration(), connectIntegration(), + genericPoolIntegration(), ]; } @@ -61,5 +63,6 @@ export function getOpenTelemetryInstrumentationToPreload(): (((options?: any) => instrumentHapi, instrumentGraphql, instrumentRedis, + instrumentGenericPool, ]; } diff --git a/packages/node/src/sdk/initOtel.ts b/packages/node/src/sdk/initOtel.ts index 03d8cea76fac..37b94ebc439f 100644 --- a/packages/node/src/sdk/initOtel.ts +++ b/packages/node/src/sdk/initOtel.ts @@ -10,6 +10,7 @@ import { import { SDK_VERSION } from '@sentry/core'; import { SentryPropagator, SentrySampler, SentrySpanProcessor } from '@sentry/opentelemetry'; import { GLOBAL_OBJ, consoleSandbox, logger } from '@sentry/utils'; +import { createAddHookMessageChannel } from 'import-in-the-middle'; import { getOpenTelemetryInstrumentationToPreload } from '../integrations/tracing'; import { SentryContextManager } from '../otel/contextManager'; @@ -31,6 +32,26 @@ export function initOpenTelemetry(client: NodeClient): void { client.traceProvider = provider; } +type ImportInTheMiddleInitData = Pick & { + addHookMessagePort?: unknown; +}; + +interface RegisterOptions { + data?: ImportInTheMiddleInitData; + transferList?: unknown[]; +} + +function getRegisterOptions(esmHookConfig?: EsmLoaderHookOptions): RegisterOptions { + if (esmHookConfig?.onlyIncludeInstrumentedModules) { + const { addHookMessagePort } = createAddHookMessageChannel(); + // If the user supplied include, we need to use that as a starting point or use an empty array to ensure no modules + // are wrapped if they are not hooked + return { data: { addHookMessagePort, include: esmHookConfig.include || [] }, transferList: [addHookMessagePort] }; + } + + return { data: esmHookConfig }; +} + /** Initialize the ESM loader. */ export function maybeInitializeEsmLoader(esmHookConfig?: EsmLoaderHookOptions): void { const [nodeMajor = 0, nodeMinor = 0] = process.versions.node.split('.').map(Number); @@ -44,7 +65,7 @@ export function maybeInitializeEsmLoader(esmHookConfig?: EsmLoaderHookOptions): if (!GLOBAL_OBJ._sentryEsmLoaderHookRegistered && importMetaUrl) { try { // @ts-expect-error register is available in these versions - moduleModule.register('import-in-the-middle/hook.mjs', importMetaUrl, { data: esmHookConfig }); + moduleModule.register('import-in-the-middle/hook.mjs', importMetaUrl, getRegisterOptions(esmHookConfig)); GLOBAL_OBJ._sentryEsmLoaderHookRegistered = true; } catch (error) { logger.warn('Failed to register ESM hook', error); diff --git a/packages/node/src/types.ts b/packages/node/src/types.ts index 9604b31ddb22..aa9873e2da91 100644 --- a/packages/node/src/types.ts +++ b/packages/node/src/types.ts @@ -6,7 +6,18 @@ import type { NodeTransportOptions } from './transports'; export interface EsmLoaderHookOptions { include?: Array; - exclude?: Array; + exclude?: Array /** + * When set to `true`, `import-in-the-middle` will only wrap ESM modules that are specifically instrumented by + * OpenTelemetry plugins. This is useful to avoid issues where `import-in-the-middle` is not compatible with some of + * your dependencies. + * + * **Note**: This feature will only work if you `Sentry.init()` the SDK before the instrumented modules are loaded. + * This can be achieved via the Node `--import` CLI flag or by loading your app via async `import()` after calling + * `Sentry.init()`. + * + * Defaults to `false`. + */; + onlyIncludeInstrumentedModules?: boolean; } export interface BaseNodeOptions { diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 2aaa9f06dd78..688cac5489af 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -47,6 +47,7 @@ "@sentry/core": "8.28.0", "@sentry/node": "8.28.0", "@sentry/opentelemetry": "8.28.0", + "@sentry/rollup-plugin": "2.22.3", "@sentry/types": "8.28.0", "@sentry/utils": "8.28.0", "@sentry/vite-plugin": "2.22.3", diff --git a/packages/nuxt/src/vite/sourceMaps.ts b/packages/nuxt/src/vite/sourceMaps.ts index 3518c45409e0..18eed2cbcfd8 100644 --- a/packages/nuxt/src/vite/sourceMaps.ts +++ b/packages/nuxt/src/vite/sourceMaps.ts @@ -1,52 +1,102 @@ import type { Nuxt } from '@nuxt/schema'; -import { sentryVitePlugin } from '@sentry/vite-plugin'; +import { type SentryRollupPluginOptions, sentryRollupPlugin } from '@sentry/rollup-plugin'; +import { type SentryVitePluginOptions, sentryVitePlugin } from '@sentry/vite-plugin'; +import type { NitroConfig } from 'nitropack'; import type { SentryNuxtModuleOptions } from '../common/types'; /** - * Setup source maps for Sentry inside the Nuxt module during build time. + * Setup source maps for Sentry inside the Nuxt module during build time (in Vite for Nuxt and Rollup for Nitro). */ export function setupSourceMaps(moduleOptions: SentryNuxtModuleOptions, nuxt: Nuxt): void { - nuxt.hook('vite:extendConfig', async (viteInlineConfig, _env) => { - const sourceMapsUploadOptions = moduleOptions.sourceMapsUploadOptions || {}; - - if ((sourceMapsUploadOptions.enabled ?? true) && viteInlineConfig.mode !== 'development') { - const sentryPlugin = sentryVitePlugin({ - org: sourceMapsUploadOptions.org ?? process.env.SENTRY_ORG, - project: sourceMapsUploadOptions.project ?? process.env.SENTRY_PROJECT, - authToken: sourceMapsUploadOptions.authToken ?? process.env.SENTRY_AUTH_TOKEN, - telemetry: sourceMapsUploadOptions.telemetry ?? true, - sourcemaps: { - assets: sourceMapsUploadOptions.sourcemaps?.assets ?? undefined, - ignore: sourceMapsUploadOptions.sourcemaps?.ignore ?? undefined, - filesToDeleteAfterUpload: sourceMapsUploadOptions.sourcemaps?.filesToDeleteAfterUpload ?? undefined, - }, - _metaOptions: { - telemetry: { - metaFramework: 'nuxt', - }, - }, - debug: moduleOptions.debug ?? false, - }); + const sourceMapsUploadOptions = moduleOptions.sourceMapsUploadOptions || {}; + const sourceMapsEnabled = sourceMapsUploadOptions.enabled ?? true; + nuxt.hook('vite:extendConfig', async (viteInlineConfig, _env) => { + if (sourceMapsEnabled && viteInlineConfig.mode !== 'development') { + // Add Sentry plugin viteInlineConfig.plugins = viteInlineConfig.plugins || []; - viteInlineConfig.plugins.push(sentryPlugin); - - const sourceMapsPreviouslyEnabled = viteInlineConfig.build?.sourcemap; - - if (moduleOptions.debug && !sourceMapsPreviouslyEnabled) { - // eslint-disable-next-line no-console - console.log('[Sentry]: Enabled source maps generation in the Vite build options.'); - if (!moduleOptions.sourceMapsUploadOptions?.sourcemaps?.filesToDeleteAfterUpload) { - // eslint-disable-next-line no-console - console.warn( - `[Sentry] We recommend setting the \`sourceMapsUploadOptions.sourcemaps.filesToDeleteAfterUpload\` option to clean up source maps after uploading. -[Sentry] Otherwise, source maps might be deployed to production, depending on your configuration`, - ); - } - } + viteInlineConfig.plugins.push(sentryVitePlugin(getPluginOptions(moduleOptions))); + // Enable source maps viteInlineConfig.build = viteInlineConfig.build || {}; viteInlineConfig.build.sourcemap = true; + + logDebugInfo(moduleOptions, viteInlineConfig.build?.sourcemap); + } + }); + + nuxt.hook('nitro:config', (nitroConfig: NitroConfig) => { + if (sourceMapsEnabled && !nitroConfig.dev) { + if (!nitroConfig.rollupConfig) { + nitroConfig.rollupConfig = {}; + } + + if (nitroConfig.rollupConfig.plugins === null || nitroConfig.rollupConfig.plugins === undefined) { + nitroConfig.rollupConfig.plugins = []; + } else if (!Array.isArray(nitroConfig.rollupConfig.plugins)) { + // `rollupConfig.plugins` can be a single plugin, so we want to put it into an array so that we can push our own plugin + nitroConfig.rollupConfig.plugins = [nitroConfig.rollupConfig.plugins]; + } + + // Add Sentry plugin + nitroConfig.rollupConfig.plugins.push(sentryRollupPlugin(getPluginOptions(moduleOptions, true))); + + // Enable source maps + nitroConfig.rollupConfig.output = nitroConfig?.rollupConfig?.output || {}; + nitroConfig.rollupConfig.output.sourcemap = true; + nitroConfig.rollupConfig.output.sourcemapExcludeSources = false; // Adding "sourcesContent" to the source map (Nitro sets this eto `true`) + + logDebugInfo(moduleOptions, nitroConfig.rollupConfig.output?.sourcemap); } }); } + +/** + * Normalizes the beginning of a path from e.g. ../../../ to ./ + */ +function normalizePath(path: string): string { + return path.replace(/^(\.\.\/)+/, './'); +} + +function getPluginOptions( + moduleOptions: SentryNuxtModuleOptions, + isNitro = false, +): SentryVitePluginOptions | SentryRollupPluginOptions { + const sourceMapsUploadOptions = moduleOptions.sourceMapsUploadOptions || {}; + + return { + org: sourceMapsUploadOptions.org ?? process.env.SENTRY_ORG, + project: sourceMapsUploadOptions.project ?? process.env.SENTRY_PROJECT, + authToken: sourceMapsUploadOptions.authToken ?? process.env.SENTRY_AUTH_TOKEN, + telemetry: sourceMapsUploadOptions.telemetry ?? true, + sourcemaps: { + assets: + sourceMapsUploadOptions.sourcemaps?.assets ?? isNitro ? ['./.output/server/**/*'] : ['./.output/public/**/*'], + ignore: sourceMapsUploadOptions.sourcemaps?.ignore ?? undefined, + filesToDeleteAfterUpload: sourceMapsUploadOptions.sourcemaps?.filesToDeleteAfterUpload ?? undefined, + rewriteSources: (source: string) => normalizePath(source), + }, + _metaOptions: { + telemetry: { + metaFramework: 'nuxt', + }, + }, + debug: moduleOptions.debug ?? false, + }; +} + +function logDebugInfo(moduleOptions: SentryNuxtModuleOptions, sourceMapsPreviouslyEnabled: boolean): void { + if (moduleOptions.debug && !sourceMapsPreviouslyEnabled) { + // eslint-disable-next-line no-console + console.log('[Sentry]: Enabled source maps generation in the Vite build options.'); + + const sourceMapsUploadOptions = moduleOptions.sourceMapsUploadOptions || {}; + + if (!sourceMapsUploadOptions.sourcemaps?.filesToDeleteAfterUpload) { + // eslint-disable-next-line no-console + console.warn( + '[Sentry] We recommend setting the `sourceMapsUploadOptions.sourcemaps.filesToDeleteAfterUpload` option to clean up source maps after uploading. Otherwise, source maps might be deployed to production, depending on your configuration', + ); + } + } +} diff --git a/packages/remix/package.json b/packages/remix/package.json index 286926c79f1d..9b86fd631ff8 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -52,7 +52,7 @@ "access": "public" }, "dependencies": { - "@opentelemetry/instrumentation-http": "0.52.1", + "@opentelemetry/instrumentation-http": "0.53.0", "@remix-run/router": "1.x", "@sentry/cli": "^2.33.0", "@sentry/core": "8.28.0", diff --git a/packages/remix/src/index.server.ts b/packages/remix/src/index.server.ts index 7ab6efb15827..457dcb9f8685 100644 --- a/packages/remix/src/index.server.ts +++ b/packages/remix/src/index.server.ts @@ -47,6 +47,7 @@ export { flush, functionToStringIntegration, generateInstrumentOnce, + genericPoolIntegration, getActiveSpan, getAutoPerformanceIntegrations, getClient, diff --git a/packages/replay-internal/src/types/performance.ts b/packages/replay-internal/src/types/performance.ts index 7a60e51684f3..f598581c93ab 100644 --- a/packages/replay-internal/src/types/performance.ts +++ b/packages/replay-internal/src/types/performance.ts @@ -114,7 +114,7 @@ export interface WebVitalData { /** * The layout shifts of a CLS metric */ - attributions?: { value: number; sources?: number[] }[]; + attributions?: { value: number; nodeIds: number[] | undefined }[]; } /** diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index 830f878dc8ea..c28e69caee00 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -58,11 +58,6 @@ interface LayoutShiftAttribution { currentRect: DOMRectReadOnly; } -interface Attribution { - value: number; - nodeIds?: number[]; -} - /** * Handler creater for web vitals */ @@ -198,7 +193,7 @@ export function getLargestContentfulPaint(metric: Metric): ReplayPerformanceEntr return getWebVital(metric, 'largest-contentful-paint', node); } -function isLayoutShift(entry: PerformanceEntry | LayoutShift): entry is LayoutShift { +function isLayoutShift(entry: PerformanceEntry): entry is LayoutShift { return (entry as LayoutShift).sources !== undefined; } @@ -206,7 +201,7 @@ function isLayoutShift(entry: PerformanceEntry | LayoutShift): entry is LayoutSh * Add a CLS event to the replay based on a CLS metric. */ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry { - const layoutShifts: Attribution[] = []; + const layoutShifts: WebVitalData['attributions'] = []; const nodes: Node[] = []; for (const entry of metric.entries) { if (isLayoutShift(entry)) { @@ -220,9 +215,10 @@ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry } } } - layoutShifts.push({ value: entry.value, nodeIds }); + layoutShifts.push({ value: entry.value, nodeIds: nodeIds.length ? nodeIds : undefined }); } } + return getWebVital(metric, 'cumulative-layout-shift', nodes, layoutShifts); } @@ -251,14 +247,14 @@ function getWebVital( metric: Metric, name: string, nodes: Node[] | undefined, - attributions?: Attribution[], + attributions?: WebVitalData['attributions'], ): ReplayPerformanceEntry { const value = metric.value; const rating = metric.rating; const end = getAbsoluteTime(value); - const data: ReplayPerformanceEntry = { + return { type: 'web-vital', name, start: end, @@ -271,6 +267,4 @@ function getWebVital( attributions, }, }; - - return data; } diff --git a/packages/solid/README.md b/packages/solid/README.md index 06012bb34be8..e5ddd2186c02 100644 --- a/packages/solid/README.md +++ b/packages/solid/README.md @@ -4,16 +4,16 @@

    -# Official Sentry SDK for Solid (EXPERIMENTAL) +# Official Sentry SDK for Solid [![npm version](https://img.shields.io/npm/v/@sentry/solid.svg)](https://www.npmjs.com/package/@sentry/solid) [![npm dm](https://img.shields.io/npm/dm/@sentry/solid.svg)](https://www.npmjs.com/package/@sentry/solid) [![npm dt](https://img.shields.io/npm/dt/@sentry/solid.svg)](https://www.npmjs.com/package/@sentry/solid) -This SDK is considered ⚠️ **experimental and in an alpha state**. It may experience breaking changes. Please reach out -on [GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have any feedback or concerns. This -SDK currently only supports [Solid](https://www.solidjs.com/) and is not yet officially compatible with -[Solid Start](https://start.solidjs.com/). +This SDK is in **Beta**. The API is stable but updates may include minor changes in behavior. Please reach out on +[GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have any feedback or concerns. This +SDK is for [Solid](https://www.solidjs.com/). If you're using [SolidStart](https://start.solidjs.com/) see our +[SolidStart SDK here](https://github.com/getsentry/sentry-javascript/tree/develop/packages/solidstart). # Solid Router diff --git a/packages/solidstart/.eslintrc.js b/packages/solidstart/.eslintrc.js index c1f55c94aadf..d567b12530d0 100644 --- a/packages/solidstart/.eslintrc.js +++ b/packages/solidstart/.eslintrc.js @@ -14,6 +14,7 @@ module.exports = { files: ['src/vite/**', 'src/server/**'], rules: { '@sentry-internal/sdk/no-optional-chaining': 'off', + '@sentry-internal/sdk/no-nullish-coalescing': 'off', }, }, ], diff --git a/packages/solidstart/README.md b/packages/solidstart/README.md index 0b25a3a37e3e..ceda55838e8d 100644 --- a/packages/solidstart/README.md +++ b/packages/solidstart/README.md @@ -4,15 +4,15 @@

    -# Official Sentry SDK for Solid Start (EXPERIMENTAL) +# Official Sentry SDK for SolidStart [![npm version](https://img.shields.io/npm/v/@sentry/solidstart.svg)](https://www.npmjs.com/package/@sentry/solidstart) [![npm dm](https://img.shields.io/npm/dm/@sentry/solidstart.svg)](https://www.npmjs.com/package/@sentry/solidstart) [![npm dt](https://img.shields.io/npm/dt/@sentry/solidstart.svg)](https://www.npmjs.com/package/@sentry/solidstart) -This SDK is considered ⚠️ **experimental and in an alpha state**. It may experience breaking changes. Please reach out -on [GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have any feedback or concerns. This -SDK is for [Solid Start](https://start.solidjs.com/). If you're using [Solid](https://www.solidjs.com/) see our +This SDK is in **Beta**. The API is stable but updates may include minor changes in behavior. Please reach out on +[GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have any feedback or concerns. This +SDK is for [SolidStart](https://start.solidjs.com/). If you're using [Solid](https://www.solidjs.com/) see our [Solid SDK here](https://github.com/getsentry/sentry-javascript/tree/develop/packages/solid). ## Links @@ -22,7 +22,7 @@ SDK is for [Solid Start](https://start.solidjs.com/). If you're using [Solid](ht ## General This package is a wrapper around `@sentry/node` for the server and `@sentry/solid` for the client side, with added -functionality related to Solid Start. +functionality related to SolidStart. ## Manual Setup @@ -30,7 +30,7 @@ If the setup through the wizard doesn't work for you, you can also set up the SD ### 1. Prerequesits & Installation -Install the Sentry Solid Start SDK: +Install the Sentry SolidStart SDK: ```bash # Using npm @@ -157,58 +157,34 @@ render( ); ``` -# Sourcemaps and Releases +## Uploading Source Maps -To generate and upload source maps of your Solid Start app use our Vite bundler plugin. - -1. Install the Sentry Vite plugin - -```bash -# Using npm -npm install @sentry/vite-plugin --save-dev - -# Using yarn -yarn add @sentry/vite-plugin --dev -``` - -2. Configure the vite plugin - -To upload source maps you have to configure an auth token. Auth tokens can be passed to the plugin explicitly with the -`authToken` option, with a `SENTRY_AUTH_TOKEN` environment variable, or with an `.env.sentry-build-plugin` file in the -working directory when building your project. We recommend you add the auth token to your CI/CD environment as an -environment variable. +To upload source maps, add the `sentrySolidStartVite` plugin from `@sentry/solidstart` to your `app.config.ts` and +configure an auth token. Auth tokens can be passed to the plugin explicitly with the `authToken` option, with a +`SENTRY_AUTH_TOKEN` environment variable, or with an `.env.sentry-build-plugin` file in the working directory when +building your project. We recommend you add the auth token to your CI/CD environment as an environment variable. Learn more about configuring the plugin in our [Sentry Vite Plugin documentation](https://www.npmjs.com/package/@sentry/vite-plugin). -```bash -// .env.sentry-build-plugin -SENTRY_AUTH_TOKEN= -SENTRY_ORG= -SENTRY_PROJECT= -``` - -3. Finally, add the plugin to your `app.config.ts` file. - -```javascript +```typescript +// app.config.ts import { defineConfig } from '@solidjs/start/config'; -import { sentryVitePlugin } from '@sentry/vite-plugin'; +import { sentrySolidStartVite } from '@sentry/solidstart'; export default defineConfig({ - // rest of your config // ... vite: { - build: { - sourcemap: true, - }, plugins: [ - sentryVitePlugin({ + sentrySolidStartVite({ org: process.env.SENTRY_ORG, project: process.env.SENTRY_PROJECT, authToken: process.env.SENTRY_AUTH_TOKEN, + debug: true, }), ], }, + // ... }); ``` diff --git a/packages/solidstart/src/client/sdk.ts b/packages/solidstart/src/client/sdk.ts index f44a2134ce50..8b1b6f7f39d5 100644 --- a/packages/solidstart/src/client/sdk.ts +++ b/packages/solidstart/src/client/sdk.ts @@ -1,13 +1,21 @@ import { applySdkMetadata } from '@sentry/core'; import type { BrowserOptions } from '@sentry/solid'; -import { init as initSolidSDK } from '@sentry/solid'; -import type { Client } from '@sentry/types'; +import { + browserTracingIntegration, + getDefaultIntegrations as getDefaultSolidIntegrations, + init as initSolidSDK, +} from '@sentry/solid'; +import type { Client, Integration } from '@sentry/types'; + +// Treeshakable guard to remove all code related to tracing +declare const __SENTRY_TRACING__: boolean; /** * Initializes the client side of the Solid Start SDK. */ export function init(options: BrowserOptions): Client | undefined { const opts = { + defaultIntegrations: getDefaultIntegrations(options), ...options, }; @@ -15,3 +23,20 @@ export function init(options: BrowserOptions): Client | undefined { return initSolidSDK(opts); } + +function getDefaultIntegrations(options: BrowserOptions): Integration[] { + const integrations = getDefaultSolidIntegrations(options); + + // This evaluates to true unless __SENTRY_TRACING__ is text-replaced with "false", + // in which case everything inside will get tree-shaken away + if (typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__) { + // We add the default BrowserTracingIntegration here always. + // We can do this, even if `solidRouterBrowserTracingIntegration` is + // supplied as integration in `init` by users because it will win + // over the default integration by virtue of having the same + // `BrowserTracing` integration name and being added later. + integrations.push(browserTracingIntegration()); + } + + return integrations; +} diff --git a/packages/solidstart/src/index.server.ts b/packages/solidstart/src/index.server.ts index 0ce5251aa327..d675a1c72820 100644 --- a/packages/solidstart/src/index.server.ts +++ b/packages/solidstart/src/index.server.ts @@ -1 +1,2 @@ export * from './server'; +export * from './vite'; diff --git a/packages/solidstart/src/index.types.ts b/packages/solidstart/src/index.types.ts index 89eaa14662e3..51adf848775a 100644 --- a/packages/solidstart/src/index.types.ts +++ b/packages/solidstart/src/index.types.ts @@ -3,6 +3,7 @@ // exports in this file - which we do below. export * from './client'; export * from './server'; +export * from './vite'; import type { Integration, Options, StackParser } from '@sentry/types'; diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index c3499a82459a..995f58d057e3 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -38,6 +38,7 @@ export { flush, functionToStringIntegration, generateInstrumentOnce, + genericPoolIntegration, getActiveSpan, getAutoPerformanceIntegrations, getClient, diff --git a/packages/solidstart/src/vite/index.ts b/packages/solidstart/src/vite/index.ts new file mode 100644 index 000000000000..464bbd604fbe --- /dev/null +++ b/packages/solidstart/src/vite/index.ts @@ -0,0 +1 @@ +export * from './sentrySolidStartVite'; diff --git a/packages/solidstart/src/vite/sentrySolidStartVite.ts b/packages/solidstart/src/vite/sentrySolidStartVite.ts new file mode 100644 index 000000000000..59435f919071 --- /dev/null +++ b/packages/solidstart/src/vite/sentrySolidStartVite.ts @@ -0,0 +1,18 @@ +import type { Plugin } from 'vite'; +import { makeSourceMapsVitePlugin } from './sourceMaps'; +import type { SentrySolidStartPluginOptions } from './types'; + +/** + * Various Sentry vite plugins to be used for SolidStart. + */ +export const sentrySolidStartVite = (options: SentrySolidStartPluginOptions = {}): Plugin[] => { + const sentryPlugins: Plugin[] = []; + + if (process.env.NODE_ENV !== 'development') { + if (options.sourceMapsUploadOptions?.enabled ?? true) { + sentryPlugins.push(...makeSourceMapsVitePlugin(options)); + } + } + + return sentryPlugins; +}; diff --git a/packages/solidstart/src/vite/sourceMaps.ts b/packages/solidstart/src/vite/sourceMaps.ts new file mode 100644 index 000000000000..548038515e79 --- /dev/null +++ b/packages/solidstart/src/vite/sourceMaps.ts @@ -0,0 +1,58 @@ +import { sentryVitePlugin } from '@sentry/vite-plugin'; +import type { Plugin } from 'vite'; +import type { SentrySolidStartPluginOptions } from './types'; + +/** + * A Sentry plugin for SolidStart to enable source maps and use + * @sentry/vite-plugin to automatically upload source maps to Sentry. + * @param {SourceMapsOptions} options + */ +export function makeSourceMapsVitePlugin(options: SentrySolidStartPluginOptions): Plugin[] { + const { authToken, debug, org, project, sourceMapsUploadOptions } = options; + return [ + { + name: 'sentry-solidstart-source-maps', + apply: 'build', + enforce: 'post', + config(config) { + const sourceMapsPreviouslyNotEnabled = !config.build?.sourcemap; + if (debug && sourceMapsPreviouslyNotEnabled) { + // eslint-disable-next-line no-console + console.log('[Sentry SolidStart Plugin] Enabling source map generation'); + if (!sourceMapsUploadOptions?.filesToDeleteAfterUpload) { + // eslint-disable-next-line no-console + console.warn( + `[Sentry SolidStart PLugin] We recommend setting the \`sourceMapsUploadOptions.filesToDeleteAfterUpload\` option to clean up source maps after uploading. +[Sentry SolidStart Plugin] Otherwise, source maps might be deployed to production, depending on your configuration`, + ); + } + } + return { + ...config, + build: { + ...config.build, + sourcemap: true, + }, + }; + }, + }, + ...sentryVitePlugin({ + authToken: authToken ?? process.env.SENTRY_AUTH_TOKEN, + bundleSizeOptimizations: options.bundleSizeOptimizations, + debug: debug ?? false, + org: org ?? process.env.SENTRY_ORG, + project: project ?? process.env.SENTRY_PROJECT, + sourcemaps: { + filesToDeleteAfterUpload: sourceMapsUploadOptions?.filesToDeleteAfterUpload ?? undefined, + ...sourceMapsUploadOptions?.unstable_sentryVitePluginOptions?.sourcemaps, + }, + telemetry: sourceMapsUploadOptions?.telemetry ?? true, + _metaOptions: { + telemetry: { + metaFramework: 'solidstart', + }, + }, + ...sourceMapsUploadOptions?.unstable_sentryVitePluginOptions, + }), + ]; +} diff --git a/packages/solidstart/src/vite/types.ts b/packages/solidstart/src/vite/types.ts new file mode 100644 index 000000000000..4a64e4856b5d --- /dev/null +++ b/packages/solidstart/src/vite/types.ts @@ -0,0 +1,128 @@ +import type { SentryVitePluginOptions } from '@sentry/vite-plugin'; + +type SourceMapsOptions = { + /** + * If this flag is `true`, and an auth token is detected, the Sentry SDK will + * automatically generate and upload source maps to Sentry during a production build. + * + * @default true + */ + enabled?: boolean; + + /** + * If this flag is `true`, the Sentry plugin will collect some telemetry data and send it to Sentry. + * It will not collect any sensitive or user-specific data. + * + * @default true + */ + telemetry?: boolean; + + /** + * A glob or an array of globs that specifies the build artifacts that should be deleted after the artifact + * upload to Sentry has been completed. + * + * @default [] - By default no files are deleted. + * + * The globbing patterns follow the implementation of the glob package. (https://www.npmjs.com/package/glob) + */ + filesToDeleteAfterUpload?: string | Array; + + /** + * Options to further customize the Sentry Vite Plugin (@sentry/vite-plugin) behavior directly. + * Options specified in this object take precedence over the options specified in + * the `sourcemaps` and `release` objects. + * + * @see https://www.npmjs.com/package/@sentry/vite-plugin/v/2.22.2#options which lists all available options. + * + * Warning: Options within this object are subject to change at any time. + * We DO NOT guarantee semantic versioning for these options, meaning breaking + * changes can occur at any time within a major SDK version. + * + * Furthermore, some options are untested with SvelteKit specifically. Use with caution. + */ + unstable_sentryVitePluginOptions?: Partial; +}; + +type BundleSizeOptimizationOptions = { + /** + * If set to `true`, the plugin will attempt to tree-shake (remove) any debugging code within the Sentry SDK. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * Setting this option to `true` will disable features like the SDK's `debug` option. + */ + excludeDebugStatements?: boolean; + + /** + * If set to true, the plugin will try to tree-shake tracing statements out. + * Note that the success of this depends on tree shaking generally being enabled in your build. + * Attention: DO NOT enable this when you're using any performance monitoring-related SDK features (e.g. Sentry.startSpan()). + */ + excludeTracing?: boolean; + + /** + * If set to `true`, the plugin will attempt to tree-shake (remove) code related to the Sentry SDK's Session Replay Shadow DOM recording functionality. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * This option is safe to be used when you do not want to capture any Shadow DOM activity via Sentry Session Replay. + */ + excludeReplayShadowDom?: boolean; + + /** + * If set to `true`, the plugin will attempt to tree-shake (remove) code related to the Sentry SDK's Session Replay `iframe` recording functionality. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * You can safely do this when you do not want to capture any `iframe` activity via Sentry Session Replay. + */ + excludeReplayIframe?: boolean; + + /** + * If set to `true`, the plugin will attempt to tree-shake (remove) code related to the Sentry SDK's Session Replay's Compression Web Worker. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * **Notice:** You should only do use this option if you manually host a compression worker and configure it in your Sentry Session Replay integration config via the `workerUrl` option. + */ + excludeReplayWorker?: boolean; +}; + +/** + * Build options for the Sentry module. These options are used during build-time by the Sentry SDK. + */ +export type SentrySolidStartPluginOptions = { + /** + * The auth token to use when uploading source maps to Sentry. + * + * Instead of specifying this option, you can also set the `SENTRY_AUTH_TOKEN` environment variable. + * + * To create an auth token, follow this guide: + * @see https://docs.sentry.io/product/accounts/auth-tokens/#organization-auth-tokens + */ + authToken?: string; + + /** + * The organization slug of your Sentry organization. + * Instead of specifying this option, you can also set the `SENTRY_ORG` environment variable. + */ + org?: string; + + /** + * The project slug of your Sentry project. + * Instead of specifying this option, you can also set the `SENTRY_PROJECT` environment variable. + */ + project?: string; + + /** + * Options for the Sentry Vite plugin to customize the source maps upload process. + */ + sourceMapsUploadOptions?: SourceMapsOptions; + + /** + * Options for the Sentry Vite plugin to customize bundle size optimizations. + */ + bundleSizeOptimizations?: BundleSizeOptimizationOptions; + + /** + * Enable debug functionality of the SDK during build-time. + * Enabling this will give you, for example logs about source maps. + */ + debug?: boolean; +}; diff --git a/packages/solidstart/test/client/sdk.test.ts b/packages/solidstart/test/client/sdk.test.ts index 886bb29b515d..b242e03ceb70 100644 --- a/packages/solidstart/test/client/sdk.test.ts +++ b/packages/solidstart/test/client/sdk.test.ts @@ -3,6 +3,7 @@ import * as SentrySolid from '@sentry/solid'; import { vi } from 'vitest'; import { init as solidStartInit } from '../../src/client'; +import { solidRouterBrowserTracingIntegration } from '../../src/client/solidrouter'; const browserInit = vi.spyOn(SentrySolid, 'init'); @@ -34,3 +35,47 @@ describe('Initialize Solid Start SDK', () => { expect(browserInit).toHaveBeenLastCalledWith(expect.objectContaining(expectedMetadata)); }); }); + +describe('browserTracingIntegration', () => { + it('adds the `browserTracingIntegration` when `__SENTRY_TRACING__` is not set', () => { + const client = solidStartInit({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }); + + const browserTracingIntegration = client + ?.getOptions() + .integrations.find(integration => integration.name === 'BrowserTracing'); + expect(browserTracingIntegration).toBeDefined(); + expect(browserTracingIntegration!.isDefaultInstance).toEqual(true); + }); + + it("doesn't add the `browserTracingIntegration` if `__SENTRY_TRACING__` is false", () => { + // @ts-expect-error Test setup for build-time flag + globalThis.__SENTRY_TRACING__ = false; + + const client = solidStartInit({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }); + + const browserTracingIntegration = client + ?.getOptions() + .integrations.find(integration => integration.name === 'BrowserTracing'); + expect(browserTracingIntegration).toBeUndefined(); + + // @ts-expect-error Test setup for build-time flag + delete globalThis.__SENTRY_TRACING__; + }); + + it("doesn't add the default `browserTracingIntegration` if `solidBrowserTracingIntegration` was already passed in", () => { + const client = solidStartInit({ + integrations: [solidRouterBrowserTracingIntegration()], + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }); + + const browserTracingIntegration = client + ?.getOptions() + .integrations.find(integration => integration.name === 'BrowserTracing'); + expect(browserTracingIntegration).toBeDefined(); + expect(browserTracingIntegration!.isDefaultInstance).toBeUndefined(); + }); +}); diff --git a/packages/solidstart/test/server/withServerActionInstrumentation.test.ts b/packages/solidstart/test/server/withServerActionInstrumentation.test.ts index 9a5b1e0c2b51..7e1686e2ccb1 100644 --- a/packages/solidstart/test/server/withServerActionInstrumentation.test.ts +++ b/packages/solidstart/test/server/withServerActionInstrumentation.test.ts @@ -10,7 +10,6 @@ import { spanToJSON, } from '@sentry/node'; import { NodeClient } from '@sentry/node'; -import { solidRouterBrowserTracingIntegration } from '@sentry/solidstart/solidrouter'; import { redirect } from '@solidjs/router'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -98,7 +97,6 @@ describe('withServerActionInstrumentation', () => { setCurrentClient(client); client.on('spanStart', span => spanStartMock(spanToJSON(span))); - client.addIntegration(solidRouterBrowserTracingIntegration()); await serverActionGetPrefecture(); expect(spanStartMock).toHaveBeenCalledWith( diff --git a/packages/solidstart/test/vite/sentrySolidStartVite.test.ts b/packages/solidstart/test/vite/sentrySolidStartVite.test.ts new file mode 100644 index 000000000000..d3f905313859 --- /dev/null +++ b/packages/solidstart/test/vite/sentrySolidStartVite.test.ts @@ -0,0 +1,50 @@ +import type { Plugin } from 'vite'; +import { describe, expect, it, vi } from 'vitest'; +import { sentrySolidStartVite } from '../../src/vite/sentrySolidStartVite'; + +vi.spyOn(console, 'log').mockImplementation(() => { + /* noop */ +}); +vi.spyOn(console, 'warn').mockImplementation(() => { + /* noop */ +}); + +function getSentrySolidStartVitePlugins(options?: Parameters[0]): Plugin[] { + return sentrySolidStartVite({ + project: 'project', + org: 'org', + authToken: 'token', + ...options, + }); +} + +describe('sentrySolidStartVite()', () => { + it('returns an array of vite plugins', () => { + const plugins = getSentrySolidStartVitePlugins(); + const names = plugins.map(plugin => plugin.name); + expect(names).toEqual([ + 'sentry-solidstart-source-maps', + 'sentry-telemetry-plugin', + 'sentry-vite-release-injection-plugin', + 'sentry-debug-id-upload-plugin', + 'sentry-vite-debug-id-injection-plugin', + 'sentry-vite-debug-id-upload-plugin', + 'sentry-file-deletion-plugin', + ]); + }); + + it("returns an empty array if source maps upload isn't enabled", () => { + const plugins = getSentrySolidStartVitePlugins({ sourceMapsUploadOptions: { enabled: false } }); + expect(plugins).toHaveLength(0); + }); + + it('returns an empty array if `NODE_ENV` is development', async () => { + const previousEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'development'; + + const plugins = getSentrySolidStartVitePlugins({ sourceMapsUploadOptions: { enabled: true } }); + expect(plugins).toHaveLength(0); + + process.env.NODE_ENV = previousEnv; + }); +}); diff --git a/packages/solidstart/test/vite/sourceMaps.test.ts b/packages/solidstart/test/vite/sourceMaps.test.ts new file mode 100644 index 000000000000..e7d6c1bd598d --- /dev/null +++ b/packages/solidstart/test/vite/sourceMaps.test.ts @@ -0,0 +1,96 @@ +import type { SentryVitePluginOptions } from '@sentry/vite-plugin'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { makeSourceMapsVitePlugin } from '../../src/vite/sourceMaps'; + +const mockedSentryVitePlugin = { + name: 'sentry-vite-debug-id-upload-plugin', + writeBundle: vi.fn(), +}; + +const sentryVitePluginSpy = vi.fn((_options: SentryVitePluginOptions) => [mockedSentryVitePlugin]); + +vi.mock('@sentry/vite-plugin', async () => { + const original = (await vi.importActual('@sentry/vite-plugin')) as any; + + return { + ...original, + sentryVitePlugin: (options: SentryVitePluginOptions) => sentryVitePluginSpy(options), + }; +}); + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe('makeSourceMapsVitePlugin()', () => { + it('returns a plugin to set `sourcemaps` to `true`', () => { + const [sourceMapsConfigPlugin, sentryVitePlugin] = makeSourceMapsVitePlugin({}); + + expect(sourceMapsConfigPlugin?.name).toEqual('sentry-solidstart-source-maps'); + expect(sourceMapsConfigPlugin?.apply).toEqual('build'); + expect(sourceMapsConfigPlugin?.enforce).toEqual('post'); + expect(sourceMapsConfigPlugin?.config).toEqual(expect.any(Function)); + + expect(sentryVitePlugin).toEqual(mockedSentryVitePlugin); + }); + + it('passes user-specified vite plugin options to vite plugin plugin', () => { + makeSourceMapsVitePlugin({ + org: 'my-org', + authToken: 'my-token', + sourceMapsUploadOptions: { + filesToDeleteAfterUpload: ['baz/*.js'], + }, + bundleSizeOptimizations: { + excludeTracing: true, + }, + }); + + expect(sentryVitePluginSpy).toHaveBeenCalledWith( + expect.objectContaining({ + org: 'my-org', + authToken: 'my-token', + sourcemaps: { + filesToDeleteAfterUpload: ['baz/*.js'], + }, + bundleSizeOptimizations: { + excludeTracing: true, + }, + }), + ); + }); + + it('should override options with unstable_sentryVitePluginOptions', () => { + makeSourceMapsVitePlugin({ + org: 'my-org', + authToken: 'my-token', + bundleSizeOptimizations: { + excludeTracing: true, + }, + sourceMapsUploadOptions: { + unstable_sentryVitePluginOptions: { + org: 'unstable-org', + sourcemaps: { + assets: ['unstable/*.js'], + }, + bundleSizeOptimizations: { + excludeTracing: false, + }, + }, + }, + }); + + expect(sentryVitePluginSpy).toHaveBeenCalledWith( + expect.objectContaining({ + org: 'unstable-org', + authToken: 'my-token', + bundleSizeOptimizations: { + excludeTracing: false, + }, + sourcemaps: { + assets: ['unstable/*.js'], + }, + }), + ); + }); +}); diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index 32dd6627d7a6..d57ec35bd7cc 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -37,6 +37,7 @@ export { fastifyIntegration, flush, functionToStringIntegration, + genericPoolIntegration, generateInstrumentOnce, getActiveSpan, getAutoPerformanceIntegrations, diff --git a/packages/utils/src/logger.ts b/packages/utils/src/logger.ts index e996d87202b2..533b59fd5882 100644 --- a/packages/utils/src/logger.ts +++ b/packages/utils/src/logger.ts @@ -1,7 +1,7 @@ import type { ConsoleLevel } from '@sentry/types'; import { DEBUG_BUILD } from './debug-build'; -import { GLOBAL_OBJ } from './worldwide'; +import { GLOBAL_OBJ, getGlobalSingleton } from './worldwide'; /** Prefix for logging strings */ const PREFIX = 'Sentry Logger '; @@ -97,4 +97,8 @@ function makeLogger(): Logger { return logger as Logger; } -export const logger = makeLogger(); +/** + * This is a logger singleton which either logs things or no-ops if logging is not enabled. + * The logger is a singleton on the carrier, to ensure that a consistent logger is used throughout the SDK. + */ +export const logger = getGlobalSingleton('logger', makeLogger); diff --git a/packages/utils/src/worldwide.ts b/packages/utils/src/worldwide.ts index e323f12034a2..2a1ca7b958d8 100644 --- a/packages/utils/src/worldwide.ts +++ b/packages/utils/src/worldwide.ts @@ -15,6 +15,7 @@ import type { Client, MetricsAggregator, Scope } from '@sentry/types'; import type { SdkSource } from './env'; +import type { logger } from './logger'; import { SDK_VERSION } from './version'; interface SentryCarrier { @@ -25,6 +26,7 @@ interface SentryCarrier { defaultIsolationScope?: Scope; defaultCurrentScope?: Scope; globalMetricsAggregators?: WeakMap | undefined; + logger?: typeof logger; /** Overwrites TextEncoder used in `@sentry/utils`, need for `react-native@0.73` and older */ encodePolyfill?: (input: string) => Uint8Array; diff --git a/packages/vue/src/tracing.ts b/packages/vue/src/tracing.ts index 70f662559adf..135b7fa8c9cc 100644 --- a/packages/vue/src/tracing.ts +++ b/packages/vue/src/tracing.ts @@ -114,6 +114,8 @@ export const createTracingMixins = (options: TracingOptions): Mixins => { attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.vue', }, + // UI spans should only be created if there is an active root span (transaction) + onlyIfParent: true, }); } } else { diff --git a/yarn.lock b/yarn.lock index 6f2432d7054a..246bb480abd9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7004,6 +7004,13 @@ dependencies: "@opentelemetry/api" "^1.0.0" +"@opentelemetry/api-logs@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.53.0.tgz#c478cbd8120ec2547b64edfa03a552cfe42170be" + integrity sha512-8HArjKx+RaAI8uEIgcORbZIPklyh1YLjPSBus8hjRmvLi6DeFzgOcdZ7KwPabKj8mXF8dX0hyfAyGfycz0DbFw== + dependencies: + "@opentelemetry/api" "^1.0.0" + "@opentelemetry/api@^0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.12.0.tgz#0359c3926e8f16fdcd8c78f196bd1e9fc4e66777" @@ -7047,6 +7054,13 @@ dependencies: "@opentelemetry/semantic-conventions" "1.25.1" +"@opentelemetry/core@1.26.0": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.26.0.tgz#7d84265aaa850ed0ca5813f97d831155be42b328" + integrity sha512-1iKxXXE8415Cdv0yjG3G6hQnB5eVEsJce3QaawX8SjDn0mAS0ZM8fAbZZJD4ajvhC15cePvosSCut404KrIIvQ== + dependencies: + "@opentelemetry/semantic-conventions" "1.27.0" + "@opentelemetry/core@^0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-0.12.0.tgz#a888badc9a408fa1f13976a574e69d14be32488e" @@ -7056,178 +7070,185 @@ "@opentelemetry/context-base" "^0.12.0" semver "^7.1.3" -"@opentelemetry/instrumentation-aws-lambda@0.43.0": - version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.43.0.tgz#6a96d582627873bb34d197655b7b69792f0f8da3" - integrity sha512-pSxcWlsE/pCWQRIw92sV2C+LmKXelYkjkA7C5s39iPUi4pZ2lA1nIiw+1R/y2pDEhUHcaKkNyljQr3cx9ZpVlQ== +"@opentelemetry/instrumentation-aws-lambda@0.44.0": + version "0.44.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.44.0.tgz#9b82bd6cc86f572be837578b29ef6bf242eb1a39" + integrity sha512-6vmr7FJIuopZzsQjEQTp4xWtF6kBp7DhD7pPIN8FN0dKUKyuVraABIpgWjMfelaUPy4rTYUGkYqPluhG0wx8Dw== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/instrumentation" "^0.53.0" "@opentelemetry/propagator-aws-xray" "^1.3.1" "@opentelemetry/resources" "^1.8.0" - "@opentelemetry/semantic-conventions" "^1.22.0" - "@types/aws-lambda" "8.10.122" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@types/aws-lambda" "8.10.143" -"@opentelemetry/instrumentation-aws-sdk@0.43.1": - version "0.43.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.43.1.tgz#b7cb8601061d57a9290cad8c499bda135ba3ba2c" - integrity sha512-qLT2cCniJ5W+6PFzKbksnoIQuq9pS83nmgaExfUwXVvlwi0ILc50dea0tWBHZMkdIDa/zZdcuFrJ7+fUcSnRow== +"@opentelemetry/instrumentation-aws-sdk@0.44.0": + version "0.44.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.44.0.tgz#f1a2d8c186d37fae42954921bbdcc3555aac331c" + integrity sha512-HIWFg4TDQsayceiikOnruMmyQ0SZYW6WiR+wknWwWVLHC3lHTCpAnqzp5V42ckArOdlwHZu2Jvq2GMSM4Myx3w== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/propagation-utils" "^0.30.10" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/propagation-utils" "^0.30.11" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-connect@0.38.0": - version "0.38.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.38.0.tgz#1f4aa27894eac2538fb3c8fce7b1be92cae0217e" - integrity sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg== +"@opentelemetry/instrumentation-connect@0.39.0": + version "0.39.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.39.0.tgz#32bdbaac464cba061c95df6c850ee81efdd86f8b" + integrity sha512-pGBiKevLq7NNglMgqzmeKczF4XQMTOUOTkK8afRHMZMnrK3fcETyTH7lVaSozwiOM3Ws+SuEmXZT7DYrrhxGlg== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" "@types/connect" "3.4.36" -"@opentelemetry/instrumentation-express@0.41.1": - version "0.41.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.41.1.tgz#658561df6ffbae86f5ad33e8d7ef2abb7b4967fc" - integrity sha512-uRx0V3LPGzjn2bxAnV8eUsDT82vT7NTwI0ezEuPMBOTOsnPpGhWdhcdNdhH80sM4TrWrOfXm9HGEdfWE3TRIww== +"@opentelemetry/instrumentation-express@0.42.0": + version "0.42.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.42.0.tgz#279f195aa66baee2b98623a16666c6229c8e7564" + integrity sha512-YNcy7ZfGnLsVEqGXQPT+S0G1AE46N21ORY7i7yUQyfhGAL4RBjnZUqefMI0NwqIl6nGbr1IpF0rZGoN8Q7x12Q== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-fastify@0.38.0": - version "0.38.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.38.0.tgz#0cb02ee1156197075e8a90e4fd18a6b6c94221ba" - integrity sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw== +"@opentelemetry/instrumentation-fastify@0.39.0": + version "0.39.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.39.0.tgz#96a040e4944daf77c53a8fe5a128bc3b2568e4aa" + integrity sha512-SS9uSlKcsWZabhBp2szErkeuuBDgxOUlllwkS92dVaWRnMmwysPhcEgHKB8rUe3BHg/GnZC1eo1hbTZv4YhfoA== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-fs@0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.14.0.tgz#19f1cb38a8c2d05f3b96af67f1c8d43f0af2829b" - integrity sha512-pVc8P5AgliC1DphyyBUgsxXlm2XaPH4BpYvt7rAZDMIqUpRk8gs19SioABtKqqxvFzg5jPtgJfJsdxq0Y+maLw== +"@opentelemetry/instrumentation-fs@0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.15.0.tgz#41658507860f39fee5209bca961cea8d24ca2a83" + integrity sha512-JWVKdNLpu1skqZQA//jKOcKdJC66TWKqa2FUFq70rKohvaSq47pmXlnabNO+B/BvLfmidfiaN35XakT5RyMl2Q== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/instrumentation" "^0.53.0" -"@opentelemetry/instrumentation-graphql@0.42.0": - version "0.42.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.42.0.tgz#588a18c39e3b3f655bc09243566172ab0b638d35" - integrity sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q== +"@opentelemetry/instrumentation-generic-pool@0.39.0": + version "0.39.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.39.0.tgz#2b9af16ad82d5cbe67125c0125753cecd162a728" + integrity sha512-y4v8Y+tSfRB3NNBvHjbjrn7rX/7sdARG7FuK6zR8PGb28CTa0kHpEGCJqvL9L8xkTNvTXo+lM36ajFGUaK1aNw== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/instrumentation" "^0.53.0" -"@opentelemetry/instrumentation-hapi@0.40.0": - version "0.40.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.40.0.tgz#ae11190f0f57cdb4dc8d792cb8bca61e5343684c" - integrity sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw== +"@opentelemetry/instrumentation-graphql@0.43.0": + version "0.43.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.43.0.tgz#71bb94ea775c70dbd388c739b397ec1418f3f170" + integrity sha512-aI3YMmC2McGd8KW5du1a2gBA0iOMOGLqg4s9YjzwbjFwjlmMNFSK1P3AIg374GWg823RPUGfVTIgZ/juk9CVOA== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + +"@opentelemetry/instrumentation-hapi@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.41.0.tgz#de8711907256d8fae1b5faf71fc825cef4a7ddbb" + integrity sha512-jKDrxPNXDByPlYcMdZjNPYCvw0SQJjN+B1A+QH+sx+sAHsKSAf9hwFiJSrI6C4XdOls43V/f/fkp9ITkHhKFbQ== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-http@0.52.1": - version "0.52.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.52.1.tgz#12061501601838d1c912f9c29bdd40a13a7e44cf" - integrity sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q== +"@opentelemetry/instrumentation-http@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.53.0.tgz#0d806adf1b3aba036bc46e16162e3c0dbb8a6b60" + integrity sha512-H74ErMeDuZfj7KgYCTOFGWF5W9AfaPnqLQQxeFq85+D29wwV2yqHbz2IKLYpkOh7EI6QwDEl7rZCIxjJLyc/CQ== dependencies: - "@opentelemetry/core" "1.25.1" - "@opentelemetry/instrumentation" "0.52.1" - "@opentelemetry/semantic-conventions" "1.25.1" + "@opentelemetry/core" "1.26.0" + "@opentelemetry/instrumentation" "0.53.0" + "@opentelemetry/semantic-conventions" "1.27.0" semver "^7.5.2" -"@opentelemetry/instrumentation-ioredis@0.42.0": - version "0.42.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.42.0.tgz#0f488ffc68af3caa474e2f67861759075170729c" - integrity sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw== +"@opentelemetry/instrumentation-ioredis@0.43.0": + version "0.43.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.43.0.tgz#dbadabaeefc4cb47c406f878444f1bcac774fa89" + integrity sha512-i3Dke/LdhZbiUAEImmRG3i7Dimm/BD7t8pDDzwepSvIQ6s2X6FPia7561gw+64w+nx0+G9X14D7rEfaMEmmjig== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/instrumentation" "^0.53.0" "@opentelemetry/redis-common" "^0.36.2" - "@opentelemetry/semantic-conventions" "^1.23.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-koa@0.42.0": - version "0.42.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.42.0.tgz#1c180f3605448c2e57a4ba073b69ffba7b2970b3" - integrity sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw== +"@opentelemetry/instrumentation-koa@0.43.0": + version "0.43.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.43.0.tgz#963fd192a1b5f6cbae5dabf4ec82e3105cbb23b1" + integrity sha512-lDAhSnmoTIN6ELKmLJBplXzT/Jqs5jGZehuG22EdSMaTwgjMpxMDI1YtlKEhiWPWkrz5LUsd0aOO0ZRc9vn3AQ== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-mongodb@0.46.0": - version "0.46.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.46.0.tgz#e3720e8ca3ca9f228fbf02f0812f7518c030b05e" - integrity sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA== +"@opentelemetry/instrumentation-mongodb@0.47.0": + version "0.47.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.47.0.tgz#f8107d878281433905e717f223fb4c0f10356a7b" + integrity sha512-yqyXRx2SulEURjgOQyJzhCECSh5i1uM49NUaq9TqLd6fA7g26OahyJfsr9NE38HFqGRHpi4loyrnfYGdrsoVjQ== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/instrumentation" "^0.53.0" "@opentelemetry/sdk-metrics" "^1.9.1" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-mongoose@0.40.0": - version "0.40.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.40.0.tgz#9c888312e524c381bfdf56a094c799150332dd51" - integrity sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew== +"@opentelemetry/instrumentation-mongoose@0.42.0": + version "0.42.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.42.0.tgz#375afd21adfcd897a8f521c1ffd2d91e6a428705" + integrity sha512-AnWv+RaR86uG3qNEMwt3plKX1ueRM7AspfszJYVkvkehiicC3bHQA6vWdb6Zvy5HAE14RyFbu9+2hUUjR2NSyg== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-mysql2@0.40.0": - version "0.40.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.40.0.tgz#fa2992c36d54427dccea68e5c69fff01103dabe6" - integrity sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg== +"@opentelemetry/instrumentation-mysql2@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.41.0.tgz#6377b6e2d2487fd88e1d79aa03658db6c8d51651" + integrity sha512-REQB0x+IzVTpoNgVmy5b+UnH1/mDByrneimP6sbDHkp1j8QOl1HyWOrBH/6YWR0nrbU3l825Em5PlybjT3232g== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" "@opentelemetry/sql-common" "^0.40.1" -"@opentelemetry/instrumentation-mysql@0.40.0": - version "0.40.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.40.0.tgz#bde5894c8eb447a4b8e940b030b2b73898da03fa" - integrity sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA== +"@opentelemetry/instrumentation-mysql@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.41.0.tgz#2d50691ead5219774bd36d66c35d5b4681485dd7" + integrity sha512-jnvrV6BsQWyHS2qb2fkfbfSb1R/lmYwqEZITwufuRl37apTopswu9izc0b1CYRp/34tUG/4k/V39PND6eyiNvw== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" - "@types/mysql" "2.15.22" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@types/mysql" "2.15.26" -"@opentelemetry/instrumentation-nestjs-core@0.39.0": - version "0.39.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.39.0.tgz#733fef4306c796951d7ea1951b45f9df0aed234d" - integrity sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA== +"@opentelemetry/instrumentation-nestjs-core@0.40.0": + version "0.40.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.40.0.tgz#2c0e6405b56caaec32747d55c57ff9a034668ea8" + integrity sha512-WF1hCUed07vKmf5BzEkL0wSPinqJgH7kGzOjjMAiTGacofNXjb/y4KQ8loj2sNsh5C/NN7s1zxQuCgbWbVTGKg== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.23.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-pg@0.43.0": - version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.43.0.tgz#3cd94ad5144e1fd326a921280fa8bb7b49005eb5" - integrity sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw== +"@opentelemetry/instrumentation-pg@0.44.0": + version "0.44.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.44.0.tgz#1e97a0aeb2dca068ee23ce75884a0a0063a7ce3f" + integrity sha512-oTWVyzKqXud1BYEGX1loo2o4k4vaU1elr3vPO8NZolrBtFvQ34nx4HgUaexUDuEog00qQt+MLR5gws/p+JXMLQ== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" "@opentelemetry/sql-common" "^0.40.1" "@types/pg" "8.6.1" - "@types/pg-pool" "2.0.4" + "@types/pg-pool" "2.0.6" -"@opentelemetry/instrumentation-redis-4@0.41.0": - version "0.41.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.41.0.tgz#6c1b1a37c18478887f346a3bc7ef309ee9f726c0" - integrity sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg== +"@opentelemetry/instrumentation-redis-4@0.42.0": + version "0.42.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.42.0.tgz#fc01104cfe884c7546385eaae03c57a47edd19d1" + integrity sha512-NaD+t2JNcOzX/Qa7kMy68JbmoVIV37fT/fJYzLKu2Wwd+0NCxt+K2OOsOakA8GVg8lSpFdbx4V/suzZZ2Pvdjg== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/instrumentation" "^0.53.0" "@opentelemetry/redis-common" "^0.36.2" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation@0.52.1", "@opentelemetry/instrumentation@^0.49 || ^0.50 || ^0.51 || ^0.52.0", "@opentelemetry/instrumentation@^0.52.0", "@opentelemetry/instrumentation@^0.52.1": - version "0.52.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz#2e7e46a38bd7afbf03cf688c862b0b43418b7f48" - integrity sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw== +"@opentelemetry/instrumentation@0.53.0", "@opentelemetry/instrumentation@^0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.53.0.tgz#e6369e4015eb5112468a4d45d38dcada7dad892d" + integrity sha512-DMwg0hy4wzf7K73JJtl95m/e0boSoWhH07rfvHvYzQtBD3Bmv0Wc1x733vyZBqmFm8OjJD0/pfiUg1W3JjFX0A== dependencies: - "@opentelemetry/api-logs" "0.52.1" - "@types/shimmer" "^1.0.2" + "@opentelemetry/api-logs" "0.53.0" + "@types/shimmer" "^1.2.0" import-in-the-middle "^1.8.1" require-in-the-middle "^7.1.1" semver "^7.5.2" @@ -7255,6 +7276,18 @@ semver "^7.5.2" shimmer "^1.2.1" +"@opentelemetry/instrumentation@^0.49 || ^0.50 || ^0.51 || ^0.52.0", "@opentelemetry/instrumentation@^0.52.1": + version "0.52.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz#2e7e46a38bd7afbf03cf688c862b0b43418b7f48" + integrity sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw== + dependencies: + "@opentelemetry/api-logs" "0.52.1" + "@types/shimmer" "^1.0.2" + import-in-the-middle "^1.8.1" + require-in-the-middle "^7.1.1" + semver "^7.5.2" + shimmer "^1.2.1" + "@opentelemetry/otlp-transformer@^0.50.0": version "0.50.0" resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.50.0.tgz#211fe512fcce9d76042680f955336dbde3be03ef" @@ -7267,10 +7300,10 @@ "@opentelemetry/sdk-metrics" "1.23.0" "@opentelemetry/sdk-trace-base" "1.23.0" -"@opentelemetry/propagation-utils@^0.30.10": - version "0.30.10" - resolved "https://registry.yarnpkg.com/@opentelemetry/propagation-utils/-/propagation-utils-0.30.10.tgz#d3074f7365efc62845928098bb15804aca47aaf0" - integrity sha512-hhTW8pFp9PSyosYzzuUL9rdm7HF97w3OCyElufFHyUnYnKkCBbu8ne2LyF/KSdI/xZ81ubxWZs78hX4S7pLq5g== +"@opentelemetry/propagation-utils@^0.30.11": + version "0.30.11" + resolved "https://registry.yarnpkg.com/@opentelemetry/propagation-utils/-/propagation-utils-0.30.11.tgz#0a1c51cb4a2724fa41c41be07024bbb6f0aade46" + integrity sha512-rY4L/2LWNk5p/22zdunpqVmgz6uN419DsRTw5KFMa6u21tWhXS8devlMy4h8m8nnS20wM7r6yYweCNNKjgLYJw== "@opentelemetry/propagator-aws-xray@^1.3.1": version "1.25.1" @@ -7370,11 +7403,16 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.0.tgz#390eb4d42a29c66bdc30066af9035645e9bb7270" integrity sha512-M+kkXKRAIAiAP6qYyesfrC5TOmDpDVtsxuGfPcqd9B/iBrac+E14jYwrgm0yZBUIbIP2OnqC3j+UgkXLm1vxUQ== -"@opentelemetry/semantic-conventions@1.25.1", "@opentelemetry/semantic-conventions@^1.17.0", "@opentelemetry/semantic-conventions@^1.22.0", "@opentelemetry/semantic-conventions@^1.23.0", "@opentelemetry/semantic-conventions@^1.25.1": +"@opentelemetry/semantic-conventions@1.25.1", "@opentelemetry/semantic-conventions@^1.17.0", "@opentelemetry/semantic-conventions@^1.23.0", "@opentelemetry/semantic-conventions@^1.25.1": version "1.25.1" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz#0deecb386197c5e9c2c28f2f89f51fb8ae9f145e" integrity sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ== +"@opentelemetry/semantic-conventions@1.27.0", "@opentelemetry/semantic-conventions@^1.27.0": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz#1a857dcc95a5ab30122e04417148211e6f945e6c" + integrity sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg== + "@opentelemetry/semantic-conventions@^0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-0.12.0.tgz#7e392aecdbdbd5d737d3995998b120dc17589ab0" @@ -7525,10 +7563,10 @@ resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.9.1.tgz#d92bd2f7f006e0316cb4fda9d73f235965cf2c64" integrity sha512-caSOnG4kxcSkhqC/2ShV7rEoWwd3XrftokxJqOCMVvia4NYV/TPtJlS9C2os3Igxw/Qyxumj9GBQzcStzECvtQ== -"@prisma/instrumentation@5.18.0": - version "5.18.0" - resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-5.18.0.tgz#8b49e25bf3f8f756eb0c4c199b4cf8b6631db891" - integrity sha512-r074avGkpPXItk+josQPhufZEmGhUCb16PQx4ITPS40vWTpTPET4VsgCBZB2alIN6SS7pRFod2vz2M2HHEEylQ== +"@prisma/instrumentation@5.19.1": + version "5.19.1" + resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-5.19.1.tgz#146319cf85f22b7a43296f0f40cfeac55516e66e" + integrity sha512-VLnzMQq7CWroL5AeaW0Py2huiNKeoMfCH3SUxstdzPrlWQi6UQ9UrfcbUkNHlVFqOMacqy8X/8YtE0kuKDpD9w== dependencies: "@opentelemetry/api" "^1.8" "@opentelemetry/instrumentation" "^0.49 || ^0.50 || ^0.51 || ^0.52.0" @@ -8302,6 +8340,14 @@ "@sentry/cli-win32-i686" "2.33.1" "@sentry/cli-win32-x64" "2.33.1" +"@sentry/rollup-plugin@2.22.3": + version "2.22.3" + resolved "https://registry.yarnpkg.com/@sentry/rollup-plugin/-/rollup-plugin-2.22.3.tgz#18ab4b7903ee723bee4cf789b38bb3febb05faae" + integrity sha512-I1UsnYzZm5W7/Pyah2yxuMRxmzgf5iDKoptFfMaerpRO5oBhFO3tMnKSLAlYMvuXKRoYkInNv6ckkUcSOF6jig== + dependencies: + "@sentry/bundler-plugin-core" "2.22.3" + unplugin "1.0.1" + "@sentry/vite-plugin@2.22.3", "@sentry/vite-plugin@^2.22.3": version "2.22.3" resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.22.3.tgz#b52802412b6f3d8e3e56742afc9624d9babae5b6" @@ -9190,7 +9236,12 @@ resolved "https://registry.yarnpkg.com/@types/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz#5433a141730f8e1d7a8e7486458ceb8144ee5edc" integrity sha512-JOvNJUU/zjfJWcA1aHDnCKHwQjZ7VQ3UNfbcMKXrkQKKyMkJHrQ9vpSVMhgsztrtsbIRJKazMDvg2QggFVwJqw== -"@types/aws-lambda@8.10.122", "@types/aws-lambda@^8.10.62": +"@types/aws-lambda@8.10.143": + version "8.10.143" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.143.tgz#383693fbaadc6994a71d64a7c09e8c244fad8dff" + integrity sha512-u5vzlcR14ge/4pMTTMDQr3MF0wEe38B2F9o84uC4F43vN5DGTy63npRrB6jQhyt+C0lGv4ZfiRcRkqJoZuPnmg== + +"@types/aws-lambda@^8.10.62": version "8.10.122" resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.122.tgz#206c8d71b09325d26a458dba27db842afdc54df1" integrity sha512-vBkIh9AY22kVOCEKo5CJlyCgmSWvasC+SWUxL/x/vOwRobMpI/HG1xp/Ae3AqmSiZeLUbOhW0FCD3ZjqqUxmXw== @@ -9839,7 +9890,14 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.32.tgz#f6cd08939ae3ad886fcc92ef7f0109dacddf61ab" integrity sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g== -"@types/mysql@2.15.22", "@types/mysql@^2.15.21": +"@types/mysql@2.15.26": + version "2.15.26" + resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.26.tgz#f0de1484b9e2354d587e7d2bd17a873cc8300836" + integrity sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ== + dependencies: + "@types/node" "*" + +"@types/mysql@^2.15.21": version "2.15.22" resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.22.tgz#8705edb9872bf4aa9dbc004cd494e00334e5cdb4" integrity sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ== @@ -9935,10 +9993,10 @@ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb" integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== -"@types/pg-pool@2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/pg-pool/-/pg-pool-2.0.4.tgz#b5c60f678094ff3acf3442628a7f708928fcf263" - integrity sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ== +"@types/pg-pool@2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/pg-pool/-/pg-pool-2.0.6.tgz#1376d9dc5aec4bb2ec67ce28d7e9858227403c77" + integrity sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ== dependencies: "@types/pg" "*" @@ -10124,6 +10182,11 @@ resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.2.tgz#93eb2c243c351f3f17d5c580c7467ae5d686b65f" integrity sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg== +"@types/shimmer@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.2.0.tgz#9b706af96fa06416828842397a70dfbbf1c14ded" + integrity sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg== + "@types/sinon@^10.0.13": version "10.0.16" resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.16.tgz#4bf10313bd9aa8eef1e50ec9f4decd3dd455b4d3" @@ -10924,14 +10987,6 @@ "@webassemblyjs/helper-numbers" "1.11.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.1" -"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" - integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== - dependencies: - "@webassemblyjs/helper-numbers" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" @@ -10965,11 +11020,6 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== -"@webassemblyjs/helper-buffer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" - integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== - "@webassemblyjs/helper-buffer@1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" @@ -11013,16 +11063,6 @@ "@webassemblyjs/helper-wasm-bytecode" "1.11.1" "@webassemblyjs/wasm-gen" "1.11.1" -"@webassemblyjs/helper-wasm-section@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" - integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/helper-wasm-section@1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" @@ -11085,20 +11125,6 @@ "@webassemblyjs/wasm-parser" "1.11.1" "@webassemblyjs/wast-printer" "1.11.1" -"@webassemblyjs/wasm-edit@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" - integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/helper-wasm-section" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-opt" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" - "@webassemblyjs/wast-printer" "1.11.6" - "@webassemblyjs/wasm-edit@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" @@ -11124,17 +11150,6 @@ "@webassemblyjs/leb128" "1.11.1" "@webassemblyjs/utf8" "1.11.1" -"@webassemblyjs/wasm-gen@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" - integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/ieee754" "1.11.6" - "@webassemblyjs/leb128" "1.11.6" - "@webassemblyjs/utf8" "1.11.6" - "@webassemblyjs/wasm-gen@1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" @@ -11156,16 +11171,6 @@ "@webassemblyjs/wasm-gen" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1" -"@webassemblyjs/wasm-opt@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" - integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" - "@webassemblyjs/wasm-opt@1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" @@ -11188,18 +11193,6 @@ "@webassemblyjs/leb128" "1.11.1" "@webassemblyjs/utf8" "1.11.1" -"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" - integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-api-error" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/ieee754" "1.11.6" - "@webassemblyjs/leb128" "1.11.6" - "@webassemblyjs/utf8" "1.11.6" - "@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" @@ -11220,14 +11213,6 @@ "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" -"@webassemblyjs/wast-printer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" - integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@xtuc/long" "4.2.2" - "@webassemblyjs/wast-printer@1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" @@ -16901,18 +16886,10 @@ enhanced-resolve@^5.14.1: graceful-fs "^4.2.4" tapable "^2.2.0" -enhanced-resolve@^5.15.0: - version "5.15.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" - integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== - dependencies: - 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== +enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -19084,7 +19061,7 @@ generate-function@^2.3.1: dependencies: is-property "^1.0.2" -generic-pool@3.9.0: +generic-pool@3.9.0, generic-pool@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== @@ -33803,42 +33780,11 @@ webpack@5.76.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== - 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" - acorn "^8.7.1" - acorn-import-assertions "^1.9.0" - browserslist "^4.21.10" - chrome-trace-event "^1.0.2" - 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.9" - json-parse-even-better-errors "^2.3.1" - loader-runner "^4.2.0" - mime-types "^2.1.27" - neo-async "^2.6.2" - schema-utils "^3.2.0" - tapable "^2.1.1" - terser-webpack-plugin "^5.3.10" - watchpack "^2.4.0" - webpack-sources "^3.2.3" - -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== +webpack@^5.90.3, webpack@^5.94.0, webpack@~5.94.0: + version "5.94.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f" + integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg== dependencies: - "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.5" "@webassemblyjs/ast" "^1.12.1" "@webassemblyjs/wasm-edit" "^1.12.1" @@ -33847,7 +33793,7 @@ webpack@^5.92.1: acorn-import-attributes "^1.9.5" browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.17.0" + enhanced-resolve "^5.17.1" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0"