diff --git a/.size-limit.js b/.size-limit.js index ffa69d850947..61efc456029a 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -47,7 +47,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), gzip: true, - limit: '75.2 KB', + limit: '76 KB', }, { name: '@sentry/browser (incl. Tracing, Replay) - with treeshaking flags', diff --git a/CHANGELOG.md b/CHANGELOG.md index ebe392a79f3c..4e3673733f27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,13 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 9.7.0-alpha.0 + +This is an alpha release that includes experimental features which are subject to breaking changes. + +- fix(node): Ensure httpIntegration propagates traces #15735 +- feat(browser): Attach top-level domain to "Failed to fetch" errors #15729 + ## 9.6.1 - feat(deps): bump @prisma/instrumentation from 6.4.1 to 6.5.0 ([#15714](https://github.com/getsentry/sentry-javascript/pull/15714)) diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index c9d6c972481a..03e4cadc4a35 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-integration-tests", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "main": "index.js", "license": "MIT", "engines": { @@ -42,7 +42,7 @@ "@babel/preset-typescript": "^7.16.7", "@playwright/test": "~1.50.0", "@sentry-internal/rrweb": "2.34.0", - "@sentry/browser": "9.6.1", + "@sentry/browser": "9.7.0-alpha.0", "axios": "1.8.2", "babel-loader": "^8.2.2", "fflate": "0.8.2", diff --git a/dev-packages/browser-integration-tests/suites/errors/fetch/init.js b/dev-packages/browser-integration-tests/suites/errors/fetch/init.js new file mode 100644 index 000000000000..ce283e32d303 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/errors/fetch/init.js @@ -0,0 +1,13 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + transportOptions: { + fetchOptions: { + // See: https://github.com/microsoft/playwright/issues/34497 + keepalive: false, + }, + }, +}); diff --git a/dev-packages/browser-integration-tests/suites/errors/fetch/subject.js b/dev-packages/browser-integration-tests/suites/errors/fetch/subject.js new file mode 100644 index 000000000000..8bae73df7b31 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/errors/fetch/subject.js @@ -0,0 +1,45 @@ +// Based on possible TypeError exceptions from https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch + +// Network error (e.g. ad-blocked, offline, page does not exist, ...) +window.networkError = () => { + fetch('http://sentry-test-external.io/does-not-exist'); +}; + +window.networkErrorSubdomain = () => { + fetch('http://subdomain.sentry-test-external.io/does-not-exist'); +}; + +// Invalid header also produces TypeError +window.invalidHeaderName = () => { + fetch('http://sentry-test-external.io/invalid-header-name', { headers: { 'C ontent-Type': 'text/xml' } }); +}; + +// Invalid header value also produces TypeError +window.invalidHeaderValue = () => { + fetch('http://sentry-test-external.io/invalid-header-value', { headers: ['Content-Type', 'text/html', 'extra'] }); +}; + +// Invalid URL scheme +window.invalidUrlScheme = () => { + fetch('blub://sentry-test-external.io/invalid-scheme'); +}; + +// URL includes credentials +window.credentialsInUrl = () => { + fetch('https://user:password@sentry-test-external.io/credentials-in-url'); +}; + +// Invalid mode +window.invalidMode = () => { + fetch('https://sentry-test-external.io/invalid-mode', { mode: 'navigate' }); +}; + +// Invalid request method +window.invalidMethod = () => { + fetch('http://sentry-test-external.io/invalid-method', { method: 'CONNECT' }); +}; + +// No-cors mode with cors-required method +window.noCorsMethod = () => { + fetch('http://sentry-test-external.io/no-cors-method', { mode: 'no-cors', method: 'PUT' }); +}; diff --git a/dev-packages/browser-integration-tests/suites/errors/fetch/test.ts b/dev-packages/browser-integration-tests/suites/errors/fetch/test.ts new file mode 100644 index 000000000000..725d384114f7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/errors/fetch/test.ts @@ -0,0 +1,288 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequest } from '../../../utils/helpers'; + +sentryTest('handles fetch network errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('networkError()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: 'Failed to fetch (sentry-test-external.io)', + webkit: 'Load failed (sentry-test-external.io)', + firefox: 'NetworkError when attempting to fetch resource. (sentry-test-external.io)', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'onunhandledrejection', + }, + }); +}); + + +sentryTest('handles fetch network errors on subdomains @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('networkErrorSubdomain()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: 'Failed to fetch (sentry-test-external.io)', + webkit: 'Load failed (sentry-test-external.io)', + firefox: 'NetworkError when attempting to fetch resource. (sentry-test-external.io)', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'onunhandledrejection', + }, + }); +}); + +sentryTest('handles fetch invalid header name errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('invalidHeaderName()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: "Failed to execute 'fetch' on 'Window': Invalid name", + webkit: "Invalid header name: 'C ontent-Type'", + firefox: 'Window.fetch: c ontent-type is an invalid header name.', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); + +sentryTest('handles fetch invalid header value errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('invalidHeaderValue()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: + "Failed to execute 'fetch' on 'Window': Failed to read the 'headers' property from 'RequestInit': The provided value cannot be converted to a sequence.", + webkit: 'Value is not a sequence', + firefox: + "Window.fetch: Element of sequence> branch of (sequence> or record) can't be converted to a sequence.", + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); + +sentryTest('handles fetch invalid URL scheme errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + await page.route('http://sentry-test-external.io/**', route => { + return route.fulfill({ + status: 200, + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('invalidUrlScheme()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: 'Failed to fetch (sentry-test-external.io)', + webkit: 'Load failed (sentry-test-external.io)', + firefox: 'NetworkError when attempting to fetch resource. (sentry-test-external.io)', + }; + + const error = errorMap[browserName]; + + /** + * This kind of error does show a helpful warning in the console, e.g.: + * Fetch API cannot load blub://sentry-test-external.io/invalid-scheme. URL scheme "blub" is not supported. + * But it seems we cannot really access this in the SDK :( + */ + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); + +sentryTest('handles fetch credentials in url errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('credentialsInUrl()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: + "Failed to execute 'fetch' on 'Window': Request cannot be constructed from a URL that includes credentials: https://user:password@sentry-test-external.io/credentials-in-url", + webkit: 'URL is not valid or contains user credentials.', + firefox: + 'Window.fetch: https://user:password@sentry-test-external.io/credentials-in-url is an url with embedded credentials.', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); + +sentryTest('handles fetch invalid mode errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('invalidMode()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: + "Failed to execute 'fetch' on 'Window': Cannot construct a Request with a RequestInit whose mode member is set as 'navigate'.", + webkit: 'Request constructor does not accept navigate fetch mode.', + firefox: 'Window.fetch: Invalid request mode navigate.', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); + +sentryTest('handles fetch invalid request method errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('invalidMethod()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: "Failed to execute 'fetch' on 'Window': 'CONNECT' HTTP method is unsupported.", + webkit: 'Method is forbidden.', + firefox: 'Window.fetch: Invalid request method CONNECT.', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); + +sentryTest( + 'handles fetch no-cors mode with cors-required method errors @firefox', + async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('noCorsMethod()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: "Failed to execute 'fetch' on 'Window': 'PUT' is unsupported in no-cors mode.", + webkit: 'Method must be GET, POST or HEAD in no-cors mode.', + firefox: 'Window.fetch: Invalid request method PUT.', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); + }, +); diff --git a/dev-packages/bundle-analyzer-scenarios/package.json b/dev-packages/bundle-analyzer-scenarios/package.json index 26ae68f43856..a7b7f6aa0c5d 100644 --- a/dev-packages/bundle-analyzer-scenarios/package.json +++ b/dev-packages/bundle-analyzer-scenarios/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/bundle-analyzer-scenarios", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Scenarios to test bundle analysis with", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/dev-packages/bundle-analyzer-scenarios", diff --git a/dev-packages/clear-cache-gh-action/package.json b/dev-packages/clear-cache-gh-action/package.json index 422e5946a159..4f0362ba259f 100644 --- a/dev-packages/clear-cache-gh-action/package.json +++ b/dev-packages/clear-cache-gh-action/package.json @@ -1,7 +1,7 @@ { "name": "@sentry-internal/clear-cache-gh-action", "description": "An internal Github Action to clear GitHub caches.", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "license": "MIT", "engines": { "node": ">=18" diff --git a/dev-packages/e2e-tests/package.json b/dev-packages/e2e-tests/package.json index 456d1045c506..24a49a495330 100644 --- a/dev-packages/e2e-tests/package.json +++ b/dev-packages/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/e2e-tests", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "license": "MIT", "private": true, "scripts": { diff --git a/dev-packages/external-contributor-gh-action/package.json b/dev-packages/external-contributor-gh-action/package.json index 5153640b1c3e..5c87d627a708 100644 --- a/dev-packages/external-contributor-gh-action/package.json +++ b/dev-packages/external-contributor-gh-action/package.json @@ -1,7 +1,7 @@ { "name": "@sentry-internal/external-contributor-gh-action", "description": "An internal Github Action to add external contributors to the CHANGELOG.md file.", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "license": "MIT", "engines": { "node": ">=18" diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index 4d17a4a67875..1e4f37decb90 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/node-integration-tests", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "license": "MIT", "engines": { "node": ">=18" @@ -30,9 +30,9 @@ "@nestjs/common": "10.4.6", "@nestjs/core": "10.4.6", "@nestjs/platform-express": "10.4.6", - "@sentry/aws-serverless": "9.6.1", - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1", + "@sentry/aws-serverless": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0", "@types/mongodb": "^3.6.20", "@types/mysql": "^2.15.21", "@types/pg": "^8.6.5", diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/http-no-tracing-no-spans/scenario.ts b/dev-packages/node-integration-tests/suites/tracing/requests/http-no-tracing-no-spans/scenario.ts new file mode 100644 index 000000000000..77884dab80c7 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/requests/http-no-tracing-no-spans/scenario.ts @@ -0,0 +1,61 @@ +import { loggingTransport } from '@sentry-internal/node-integration-tests'; +import * as Sentry from '@sentry/node'; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracePropagationTargets: [/\/v0/, 'v1'], + integrations: [Sentry.httpIntegration({ spans: false })], + transport: loggingTransport, + // Ensure this gets a correct hint + beforeBreadcrumb(breadcrumb, hint) { + breadcrumb.data = breadcrumb.data || {}; + const req = hint?.request as { path?: string }; + breadcrumb.data.ADDED_PATH = req?.path; + return breadcrumb; + }, +}); + +import * as http from 'http'; + +async function run(): Promise { + Sentry.addBreadcrumb({ message: 'manual breadcrumb' }); + + await makeHttpRequest(`${process.env.SERVER_URL}/api/v0`); + await makeHttpGet(`${process.env.SERVER_URL}/api/v1`); + await makeHttpRequest(`${process.env.SERVER_URL}/api/v2`); + await makeHttpRequest(`${process.env.SERVER_URL}/api/v3`); + + Sentry.captureException(new Error('foo')); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +run(); + +function makeHttpRequest(url: string): Promise { + return new Promise(resolve => { + http + .request(url, httpRes => { + httpRes.on('data', () => { + // we don't care about data + }); + httpRes.on('end', () => { + resolve(); + }); + }) + .end(); + }); +} + +function makeHttpGet(url: string): Promise { + return new Promise(resolve => { + http.get(url, httpRes => { + httpRes.on('data', () => { + // we don't care about data + }); + httpRes.on('end', () => { + resolve(); + }); + }); + }); +} diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/http-no-tracing-no-spans/test.ts b/dev-packages/node-integration-tests/suites/tracing/requests/http-no-tracing-no-spans/test.ts new file mode 100644 index 000000000000..e921369467f0 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/requests/http-no-tracing-no-spans/test.ts @@ -0,0 +1,98 @@ +import { expect, test } from 'vitest'; +import { createRunner } from '../../../../utils/runner'; +import { createTestServer } from '../../../../utils/server'; + +test('outgoing http requests are correctly instrumented with tracing & spans disabled', async () => { + expect.assertions(11); + + const [SERVER_URL, closeTestServer] = await createTestServer() + .get('/api/v0', headers => { + expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})$/)); + expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000'); + expect(headers['baggage']).toEqual(expect.any(String)); + }) + .get('/api/v1', headers => { + expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})$/)); + expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000'); + expect(headers['baggage']).toEqual(expect.any(String)); + }) + .get('/api/v2', headers => { + expect(headers['baggage']).toBeUndefined(); + expect(headers['sentry-trace']).toBeUndefined(); + }) + .get('/api/v3', headers => { + expect(headers['baggage']).toBeUndefined(); + expect(headers['sentry-trace']).toBeUndefined(); + }) + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .ensureNoErrorOutput() + .expect({ + event: { + exception: { + values: [ + { + type: 'Error', + value: 'foo', + }, + ], + }, + breadcrumbs: [ + { + message: 'manual breadcrumb', + timestamp: expect.any(Number), + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v0`, + status_code: 200, + ADDED_PATH: '/api/v0', + }, + timestamp: expect.any(Number), + type: 'http', + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v1`, + status_code: 200, + ADDED_PATH: '/api/v1', + }, + timestamp: expect.any(Number), + type: 'http', + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v2`, + status_code: 200, + ADDED_PATH: '/api/v2', + }, + timestamp: expect.any(Number), + type: 'http', + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v3`, + status_code: 200, + ADDED_PATH: '/api/v3', + }, + timestamp: expect.any(Number), + type: 'http', + }, + ], + }, + }) + .start() + .completed(); + + closeTestServer(); +}); diff --git a/dev-packages/rollup-utils/package.json b/dev-packages/rollup-utils/package.json index 59747e66eb27..31f7fc538207 100644 --- a/dev-packages/rollup-utils/package.json +++ b/dev-packages/rollup-utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/rollup-utils", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Rollup utilities used at Sentry for the Sentry JavaScript SDK", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/rollup-utils", diff --git a/dev-packages/size-limit-gh-action/package.json b/dev-packages/size-limit-gh-action/package.json index e8cae2c0d453..ab261aab417c 100644 --- a/dev-packages/size-limit-gh-action/package.json +++ b/dev-packages/size-limit-gh-action/package.json @@ -1,7 +1,7 @@ { "name": "@sentry-internal/size-limit-gh-action", "description": "An internal Github Action to compare the current size of a PR against the one on develop.", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "license": "MIT", "engines": { "node": ">=18" diff --git a/dev-packages/test-utils/package.json b/dev-packages/test-utils/package.json index 3fa2b4a9cc2a..3be6fa02a366 100644 --- a/dev-packages/test-utils/package.json +++ b/dev-packages/test-utils/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "9.6.1", + "version": "9.7.0-alpha.0", "name": "@sentry-internal/test-utils", "author": "Sentry", "license": "MIT", @@ -45,7 +45,7 @@ }, "devDependencies": { "@playwright/test": "~1.50.0", - "@sentry/core": "9.6.1" + "@sentry/core": "9.7.0-alpha.0" }, "volta": { "extends": "../../package.json" diff --git a/lerna.json b/lerna.json index c2b333e65256..14c702dab5a3 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "npmClient": "yarn" } diff --git a/packages/angular/package.json b/packages/angular/package.json index d71204dd1f81..771fdb434fa3 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/angular", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Angular", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/angular", @@ -21,8 +21,8 @@ "rxjs": "^6.5.5 || ^7.x" }, "dependencies": { - "@sentry/browser": "9.6.1", - "@sentry/core": "9.6.1", + "@sentry/browser": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0", "tslib": "^2.4.1" }, "devDependencies": { diff --git a/packages/astro/package.json b/packages/astro/package.json index 70afa331ca01..37cbe01dc48a 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/astro", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Astro", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/astro", @@ -56,9 +56,9 @@ "astro": ">=3.x || >=4.0.0-beta || >=5.x" }, "dependencies": { - "@sentry/browser": "9.6.1", - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1", + "@sentry/browser": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0", "@sentry/vite-plugin": "^2.22.6" }, "devDependencies": { diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index 1ab833d89ab7..b11aeb0d9a78 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/aws-serverless", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for AWS Lambda and AWS Serverless Environments", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/serverless", @@ -68,8 +68,8 @@ "@opentelemetry/instrumentation": "^0.57.2", "@opentelemetry/instrumentation-aws-lambda": "0.50.3", "@opentelemetry/instrumentation-aws-sdk": "0.49.1", - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0", "@types/aws-lambda": "^8.10.62" }, "devDependencies": { diff --git a/packages/browser-utils/package.json b/packages/browser-utils/package.json index 13f404a7c1cd..388017f9d7a3 100644 --- a/packages/browser-utils/package.json +++ b/packages/browser-utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-utils", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Browser Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser-utils", @@ -39,7 +39,7 @@ "access": "public" }, "dependencies": { - "@sentry/core": "9.6.1" + "@sentry/core": "9.7.0-alpha.0" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/browser/package.json b/packages/browser/package.json index e124710cfbf4..7d364ef966ed 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/browser", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for browsers", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser", @@ -39,14 +39,14 @@ "access": "public" }, "dependencies": { - "@sentry-internal/browser-utils": "9.6.1", - "@sentry-internal/feedback": "9.6.1", - "@sentry-internal/replay": "9.6.1", - "@sentry-internal/replay-canvas": "9.6.1", - "@sentry/core": "9.6.1" + "@sentry-internal/browser-utils": "9.7.0-alpha.0", + "@sentry-internal/feedback": "9.7.0-alpha.0", + "@sentry-internal/replay": "9.7.0-alpha.0", + "@sentry-internal/replay-canvas": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0" }, "devDependencies": { - "@sentry-internal/integration-shims": "9.6.1", + "@sentry-internal/integration-shims": "9.7.0-alpha.0", "fake-indexeddb": "^4.0.1" }, "scripts": { diff --git a/packages/bun/package.json b/packages/bun/package.json index 01037ba9c4cb..bb8000aa6630 100644 --- a/packages/bun/package.json +++ b/packages/bun/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/bun", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for bun", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/bun", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1", - "@sentry/opentelemetry": "9.6.1" + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0", + "@sentry/opentelemetry": "9.7.0-alpha.0" }, "devDependencies": { "bun-types": "latest" diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json index e143e4e4b50e..2fae2a7ceb80 100644 --- a/packages/cloudflare/package.json +++ b/packages/cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/cloudflare", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Cloudflare Workers and Pages", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/cloudflare", @@ -49,7 +49,7 @@ "access": "public" }, "dependencies": { - "@sentry/core": "9.6.1" + "@sentry/core": "9.7.0-alpha.0" }, "peerDependencies": { "@cloudflare/workers-types": "^4.x" diff --git a/packages/core/package.json b/packages/core/package.json index c71a62cac536..97115eaf44ed 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/core", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Base implementation for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/core", diff --git a/packages/core/src/utils-hoist/instrument/fetch.ts b/packages/core/src/utils-hoist/instrument/fetch.ts index f3eee711d26d..c2ecc65d7d43 100644 --- a/packages/core/src/utils-hoist/instrument/fetch.ts +++ b/packages/core/src/utils-hoist/instrument/fetch.ts @@ -107,6 +107,28 @@ function instrumentFetch(onFetchResolved?: (response: Response) => void, skipNat addNonEnumerableProperty(error, 'framesToPop', 1); } + // We enhance the not-so-helpful "Failed to fetch" error messages with the host + // Possible messages we handle here: + // * "Failed to fetch" (chromium) + // * "Load failed" (webkit) + // * "NetworkError when attempting to fetch resource." (firefox) + if ( + error instanceof TypeError && + (error.message === 'Failed to fetch' || + error.message === 'Load failed' || + error.message === 'NetworkError when attempting to fetch resource.') + ) { + try { + const url = new URL(handlerData.fetchData.url); + // We only want to take the top-level domain, e.g. xxx.sentry.io should become sentry.io + // We do this to avoid noise when there may be dynamic subdomains + const host = url.host.split('.').slice(-2).join('.'); + error.message = `${error.message} (${host})`; + } catch { + // ignore it if errors happen here + } + } + // NOTE: If you are a Sentry user, and you are seeing this stack frame, // it means the sentry.javascript SDK caught an error invoking your application code. // This is expected behavior and NOT indicative of a bug with sentry.javascript. diff --git a/packages/deno/package.json b/packages/deno/package.json index 30ca1f2e08fc..8aaf2ae061aa 100644 --- a/packages/deno/package.json +++ b/packages/deno/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/deno", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Deno", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/deno", @@ -24,7 +24,7 @@ "/build" ], "dependencies": { - "@sentry/core": "9.6.1" + "@sentry/core": "9.7.0-alpha.0" }, "scripts": { "deno-types": "node ./scripts/download-deno-types.mjs", diff --git a/packages/ember/package.json b/packages/ember/package.json index ebbc640c9d13..d57dcabecf76 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/ember", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Ember.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/ember", @@ -32,8 +32,8 @@ "dependencies": { "@babel/core": "^7.24.4", "@embroider/macros": "^1.16.0", - "@sentry/browser": "9.6.1", - "@sentry/core": "9.6.1", + "@sentry/browser": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0", "ember-auto-import": "^2.7.2", "ember-cli-babel": "^8.2.0", "ember-cli-htmlbars": "^6.1.1", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index ae661788953e..64e2e3cd594e 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-config-sdk", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK eslint config", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-config-sdk", @@ -22,8 +22,8 @@ "access": "public" }, "dependencies": { - "@sentry-internal/eslint-plugin-sdk": "9.6.1", - "@sentry-internal/typescript": "9.6.1", + "@sentry-internal/eslint-plugin-sdk": "9.7.0-alpha.0", + "@sentry-internal/typescript": "9.7.0-alpha.0", "@typescript-eslint/eslint-plugin": "^5.48.0", "@typescript-eslint/parser": "^5.48.0", "eslint-config-prettier": "^6.11.0", diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index 25f72ba8cc39..bbaef6d68457 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-plugin-sdk", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK eslint plugin", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-plugin-sdk", diff --git a/packages/feedback/package.json b/packages/feedback/package.json index 1e0093f51606..0c60e8f82c99 100644 --- a/packages/feedback/package.json +++ b/packages/feedback/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/feedback", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Sentry SDK integration for user feedback", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/feedback", @@ -39,7 +39,7 @@ "access": "public" }, "dependencies": { - "@sentry/core": "9.6.1" + "@sentry/core": "9.7.0-alpha.0" }, "devDependencies": { "preact": "^10.19.4" diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 4ffe3582ff05..7d7fe9531be4 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/gatsby", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Gatsby.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/gatsby", @@ -45,8 +45,8 @@ "access": "public" }, "dependencies": { - "@sentry/core": "9.6.1", - "@sentry/react": "9.6.1", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/react": "9.7.0-alpha.0", "@sentry/webpack-plugin": "3.2.2" }, "peerDependencies": { diff --git a/packages/google-cloud-serverless/package.json b/packages/google-cloud-serverless/package.json index b4f5421591ff..4eaaf379f162 100644 --- a/packages/google-cloud-serverless/package.json +++ b/packages/google-cloud-serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/google-cloud-serverless", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Google Cloud Functions", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/google-cloud-serverless", @@ -48,8 +48,8 @@ "access": "public" }, "dependencies": { - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0", "@types/express": "^4.17.14" }, "devDependencies": { diff --git a/packages/integration-shims/package.json b/packages/integration-shims/package.json index 16c0b66ca8aa..210795341701 100644 --- a/packages/integration-shims/package.json +++ b/packages/integration-shims/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/integration-shims", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Shims for integrations in Sentry SDK.", "main": "build/cjs/index.js", "module": "build/esm/index.js", @@ -55,7 +55,7 @@ "url": "https://github.com/getsentry/sentry-javascript/issues" }, "dependencies": { - "@sentry/core": "9.6.1" + "@sentry/core": "9.7.0-alpha.0" }, "engines": { "node": ">=18" diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index abf091651378..b63d9e5e3b29 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nestjs", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for NestJS", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nestjs", @@ -49,8 +49,8 @@ "@opentelemetry/instrumentation": "0.57.2", "@opentelemetry/instrumentation-nestjs-core": "0.44.1", "@opentelemetry/semantic-conventions": "^1.30.0", - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1" + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0" }, "devDependencies": { "@nestjs/common": "^10.0.0", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index a41367a71f9d..cca1cefae0b1 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nextjs", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Next.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nextjs", @@ -79,12 +79,12 @@ "@opentelemetry/api": "^1.9.0", "@opentelemetry/semantic-conventions": "^1.30.0", "@rollup/plugin-commonjs": "28.0.1", - "@sentry-internal/browser-utils": "9.6.1", - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1", - "@sentry/opentelemetry": "9.6.1", - "@sentry/react": "9.6.1", - "@sentry/vercel-edge": "9.6.1", + "@sentry-internal/browser-utils": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0", + "@sentry/opentelemetry": "9.7.0-alpha.0", + "@sentry/react": "9.7.0-alpha.0", + "@sentry/vercel-edge": "9.7.0-alpha.0", "@sentry/webpack-plugin": "3.2.2", "chalk": "3.0.0", "resolve": "1.22.8", diff --git a/packages/node/package.json b/packages/node/package.json index bedb9ce87a80..d497d049d44b 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/node", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Sentry Node SDK using OpenTelemetry for performance instrumentation", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/node", @@ -96,8 +96,8 @@ "@opentelemetry/sdk-trace-base": "^1.30.1", "@opentelemetry/semantic-conventions": "^1.30.0", "@prisma/instrumentation": "6.5.0", - "@sentry/core": "9.6.1", - "@sentry/opentelemetry": "9.6.1", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/opentelemetry": "9.7.0-alpha.0", "import-in-the-middle": "^1.13.0" }, "devDependencies": { diff --git a/packages/node/src/integrations/http/SentryHttpInstrumentation.ts b/packages/node/src/integrations/http/SentryHttpInstrumentation.ts index aa1f0157f2cf..f806b54b80bb 100644 --- a/packages/node/src/integrations/http/SentryHttpInstrumentation.ts +++ b/packages/node/src/integrations/http/SentryHttpInstrumentation.ts @@ -5,6 +5,7 @@ import type { InstrumentationConfig } from '@opentelemetry/instrumentation'; import { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation'; import type { AggregationCounts, Client, RequestEventData, SanitizedRequestData, Scope } from '@sentry/core'; import { + LRUMap, addBreadcrumb, generateSpanId, getBreadcrumbLogLevelFromHttpStatusCode, @@ -12,12 +13,16 @@ import { getCurrentScope, getIsolationScope, getSanitizedUrlString, + getTraceData, httpRequestToRequestData, logger, + objectToBaggageHeader, + parseBaggageHeader, parseUrl, stripUrlQueryAndFragment, withIsolationScope, } from '@sentry/core'; +import { shouldPropagateTraceForUrl } from '@sentry/opentelemetry'; import type * as http from 'node:http'; import type { IncomingMessage, RequestOptions } from 'node:http'; import type * as https from 'node:https'; @@ -30,6 +35,12 @@ import { getRequestInfo } from './vendor/getRequestInfo'; type Http = typeof http; type Https = typeof https; +type RequestArgs = + // eslint-disable-next-line @typescript-eslint/ban-types + | [url: string | URL, options?: RequestOptions, callback?: Function] + // eslint-disable-next-line @typescript-eslint/ban-types + | [options: RequestOptions, callback?: Function]; + export type SentryHttpInstrumentationOptions = InstrumentationConfig & { /** * Whether breadcrumbs should be recorded for requests. @@ -47,6 +58,15 @@ export type SentryHttpInstrumentationOptions = InstrumentationConfig & { */ extractIncomingTraceFromHeader?: boolean; + /** + * Whether to propagate Sentry trace headers in ougoing requests. + * By default this is done by the HttpInstrumentation, but if that is not added (e.g. because tracing is disabled, ...) + * then this instrumentation can take over. + * + * @default `false` + */ + propagateTraceInOutgoingRequests?: boolean; + /** * Do not capture breadcrumbs for outgoing HTTP requests to URLs where the given callback returns `true`. * For the scope of this instrumentation, this callback only controls breadcrumb creation. @@ -91,8 +111,11 @@ const MAX_BODY_BYTE_LENGTH = 1024 * 1024; * https://github.com/open-telemetry/opentelemetry-js/blob/f8ab5592ddea5cba0a3b33bf8d74f27872c0367f/experimental/packages/opentelemetry-instrumentation-http/src/http.ts */ export class SentryHttpInstrumentation extends InstrumentationBase { + private _propagationDecisionMap: LRUMap; + public constructor(config: SentryHttpInstrumentationOptions = {}) { super('@sentry/instrumentation-http', VERSION, config); + this._propagationDecisionMap = new LRUMap(100); } /** @inheritdoc */ @@ -232,18 +255,23 @@ export class SentryHttpInstrumentation extends InstrumentationBase; + // If we are not proapgating traces, we skip touching the args for the request at all + const request: ReturnType = mergedHeaders + ? original.apply(this, getOutgoingRequestArgsWithHeaders(argsCopy, mergedHeaders)) + : original.apply(this, args); request.prependListener('response', (response: http.IncomingMessage) => { const _breadcrumbs = instrumentation.getConfig().breadcrumbs; @@ -515,3 +543,118 @@ const clientToRequestSessionAggregatesMap = new Map< Client, { [timestampRoundedToSeconds: string]: { exited: number; crashed: number; errored: number } } >(); + +/** + * If there are any headers to be added for this request, this will return the full merged headers object. + * Else, it will return void. + */ +function getMergedHeadersForRequestOptions( + url: string, + options: RequestOptions, + propagationDecisionMap: LRUMap, +): void | http.OutgoingHttpHeaders { + // Manually add the trace headers, if it applies + // Note: We do not use `propagation.inject()` here, because our propagator relies on an active span + // Which we do not have in this case + const tracePropagationTargets = getClient()?.getOptions().tracePropagationTargets; + const addedHeaders = shouldPropagateTraceForUrl(url, tracePropagationTargets, propagationDecisionMap) + ? getTraceData() + : undefined; + + if (!addedHeaders) { + return; + } + + const headers = options.headers || {}; + + const { 'sentry-trace': sentryTrace, baggage } = addedHeaders; + + // We do not want to overwrite existing header here, if it was already set + if (sentryTrace && !headers['sentry-trace']) { + headers['sentry-trace'] = sentryTrace; + } + + // For baggage, we make sure to merge this into a possibly existing header + if (baggage) { + headers['baggage'] = mergeBaggageHeaders(headers['baggage'], baggage); + } + + return headers; +} + +function getAbsoluteUrl(origin: string, path: string = '/'): string { + try { + const url = new URL(path, origin); + return url.toString(); + } catch { + // fallback: Construct it on our own + const url = `${origin}`; + + if (url.endsWith('/') && path.startsWith('/')) { + return `${url}${path.slice(1)}`; + } + + if (!url.endsWith('/') && !path.startsWith('/')) { + return `${url}/${path.slice(1)}`; + } + + return `${url}${path}`; + } +} + +function mergeBaggageHeaders( + existing: string | string[] | number | undefined, + baggage: string, +): string | string[] | number | undefined { + if (!existing) { + return baggage; + } + + const existingBaggageEntries = parseBaggageHeader(existing); + const newBaggageEntries = parseBaggageHeader(baggage); + + if (!newBaggageEntries) { + return existing; + } + + // Existing entries take precedence, ensuring order remains stable for minimal changes + const mergedBaggageEntries = { ...existingBaggageEntries }; + Object.entries(newBaggageEntries).forEach(([key, value]) => { + if (!mergedBaggageEntries[key]) { + mergedBaggageEntries[key] = value; + } + }); + + return objectToBaggageHeader(mergedBaggageEntries); +} + +function getOutgoingRequestArgsWithHeaders(originalArgs: RequestArgs, headers: http.OutgoingHttpHeaders): RequestArgs { + const argsCopy = [...originalArgs] as RequestArgs; + + const arg1 = argsCopy[0]; + + // If the first argument is a string or URL, we need to merge the headers into the options object, which is optional + if (typeof arg1 === 'string' || arg1 instanceof URL) { + const arg2 = argsCopy[1]; + + // If the second argument is an object, we just overwrite the headers there + if (typeof arg2 === 'object') { + argsCopy[1] = { + ...arg2, + headers, + }; + return argsCopy; + } + + // Else, we need to insert a new object as second argument and insert the headers there + argsCopy.splice(1, 0, { headers }); + return argsCopy; + } + + // If the first argument is an object, we just overwrite the headers there + argsCopy[0] = { + ...arg1, + headers, + }; + return argsCopy; +} diff --git a/packages/node/src/integrations/http/index.ts b/packages/node/src/integrations/http/index.ts index 0df3fc56b480..cc1cac27a975 100644 --- a/packages/node/src/integrations/http/index.ts +++ b/packages/node/src/integrations/http/index.ts @@ -160,6 +160,9 @@ export const httpIntegration = defineIntegration((options: HttpOptions = {}) => // If spans are not instrumented, it means the HttpInstrumentation has not been added // In that case, we want to handle incoming trace extraction ourselves extractIncomingTraceFromHeader: !instrumentSpans, + // If spans are not instrumented, it means the HttpInstrumentation has not been added + // In that case, we want to handle trace propagation ourselves + propagateTraceInOutgoingRequests: !instrumentSpans, }); }, }; diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 3af41cba38f5..529de39e4061 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nuxt", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Nuxt (EXPERIMENTAL)", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nuxt", @@ -43,13 +43,13 @@ }, "dependencies": { "@nuxt/kit": "^3.13.2", - "@sentry/browser": "9.6.1", - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1", - "@sentry/opentelemetry": "9.6.1", + "@sentry/browser": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0", + "@sentry/opentelemetry": "9.7.0-alpha.0", "@sentry/rollup-plugin": "3.1.2", "@sentry/vite-plugin": "2.22.6", - "@sentry/vue": "9.6.1" + "@sentry/vue": "9.7.0-alpha.0" }, "devDependencies": { "@nuxt/module-builder": "^0.8.4", diff --git a/packages/opentelemetry/package.json b/packages/opentelemetry/package.json index 7eb5febf0055..c71a3c159aa1 100644 --- a/packages/opentelemetry/package.json +++ b/packages/opentelemetry/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/opentelemetry", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry utilities for OpenTelemetry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/opentelemetry", @@ -39,7 +39,7 @@ "access": "public" }, "dependencies": { - "@sentry/core": "9.6.1" + "@sentry/core": "9.7.0-alpha.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", diff --git a/packages/profiling-node/package.json b/packages/profiling-node/package.json index 815b456b85f9..7b9744677125 100644 --- a/packages/profiling-node/package.json +++ b/packages/profiling-node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/profiling-node", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Node.js Profiling", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/profiling-node", @@ -63,8 +63,8 @@ }, "dependencies": { "@sentry-internal/node-cpu-profiler": "^2.0.0", - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1" + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0" }, "devDependencies": { "@types/node": "^18.19.1" diff --git a/packages/react-router/package.json b/packages/react-router/package.json index daf2b67b32ad..da410bc4a371 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/react-router", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for React Router (Framework)", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/react-router", @@ -34,10 +34,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "9.6.1", + "@sentry/browser": "9.7.0-alpha.0", "@sentry/cli": "^2.42.3", - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0", "@sentry/vite-plugin": "^3.2.0", "glob": "11.0.1" }, diff --git a/packages/react/package.json b/packages/react/package.json index 524a837dd05e..5914de7790ab 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/react", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for React.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/react", @@ -39,8 +39,8 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "9.6.1", - "@sentry/core": "9.6.1", + "@sentry/browser": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0", "hoist-non-react-statics": "^3.3.2" }, "peerDependencies": { diff --git a/packages/remix/package.json b/packages/remix/package.json index 8f6479cd12b2..5e97264f6f20 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/remix", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Remix", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/remix", @@ -61,10 +61,10 @@ "@opentelemetry/api": "^1.9.0", "@remix-run/router": "1.x", "@sentry/cli": "^2.42.3", - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1", - "@sentry/opentelemetry": "9.6.1", - "@sentry/react": "9.6.1", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0", + "@sentry/opentelemetry": "9.7.0-alpha.0", + "@sentry/react": "9.7.0-alpha.0", "glob": "^10.3.4", "opentelemetry-instrumentation-remix": "0.8.0", "yargs": "^17.6.0" diff --git a/packages/replay-canvas/package.json b/packages/replay-canvas/package.json index a2c9da7226ba..2f2d7e51aaa3 100644 --- a/packages/replay-canvas/package.json +++ b/packages/replay-canvas/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/replay-canvas", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Replay canvas integration", "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", @@ -68,8 +68,8 @@ "@sentry-internal/rrweb": "2.34.0" }, "dependencies": { - "@sentry-internal/replay": "9.6.1", - "@sentry/core": "9.6.1" + "@sentry-internal/replay": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0" }, "engines": { "node": ">=18" diff --git a/packages/replay-internal/package.json b/packages/replay-internal/package.json index b053422ef4df..ad15aa2a428e 100644 --- a/packages/replay-internal/package.json +++ b/packages/replay-internal/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/replay", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "User replays for Sentry", "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", @@ -70,7 +70,7 @@ "homepage": "https://docs.sentry.io/platforms/javascript/session-replay/", "devDependencies": { "@babel/core": "^7.17.5", - "@sentry-internal/replay-worker": "9.6.1", + "@sentry-internal/replay-worker": "9.7.0-alpha.0", "@sentry-internal/rrweb": "2.34.0", "@sentry-internal/rrweb-snapshot": "2.34.0", "fflate": "0.8.2", @@ -78,8 +78,8 @@ "jsdom-worker": "^0.2.1" }, "dependencies": { - "@sentry-internal/browser-utils": "9.6.1", - "@sentry/core": "9.6.1" + "@sentry-internal/browser-utils": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0" }, "engines": { "node": ">=18" diff --git a/packages/replay-worker/package.json b/packages/replay-worker/package.json index 803e0c4c0261..3b55e258e033 100644 --- a/packages/replay-worker/package.json +++ b/packages/replay-worker/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/replay-worker", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Worker for @sentry-internal/replay", "main": "build/esm/index.js", "module": "build/esm/index.js", diff --git a/packages/solid/package.json b/packages/solid/package.json index 3935a888a42b..26734cd601b0 100644 --- a/packages/solid/package.json +++ b/packages/solid/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/solid", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Solid", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/solid", @@ -44,8 +44,8 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "9.6.1", - "@sentry/core": "9.6.1" + "@sentry/browser": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0" }, "peerDependencies": { "@solidjs/router": "^0.13.4", diff --git a/packages/solidstart/package.json b/packages/solidstart/package.json index 98def21083c2..f7af04e1c54c 100644 --- a/packages/solidstart/package.json +++ b/packages/solidstart/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/solidstart", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Solid Start", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/solidstart", @@ -66,10 +66,10 @@ } }, "dependencies": { - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1", - "@sentry/opentelemetry": "9.6.1", - "@sentry/solid": "9.6.1", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0", + "@sentry/opentelemetry": "9.7.0-alpha.0", + "@sentry/solid": "9.7.0-alpha.0", "@sentry/vite-plugin": "2.22.6" }, "devDependencies": { diff --git a/packages/svelte/package.json b/packages/svelte/package.json index e105bd3cef79..f13c14fa5646 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/svelte", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Svelte", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/svelte", @@ -39,8 +39,8 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "9.6.1", - "@sentry/core": "9.6.1", + "@sentry/browser": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0", "magic-string": "^0.30.0" }, "peerDependencies": { diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index b831e173c733..f37a2b6375ad 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/sveltekit", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for SvelteKit", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/sveltekit", @@ -45,11 +45,11 @@ }, "dependencies": { "@babel/parser": "7.26.9", - "@sentry/cloudflare": "9.6.1", - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1", - "@sentry/opentelemetry": "9.6.1", - "@sentry/svelte": "9.6.1", + "@sentry/cloudflare": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0", + "@sentry/opentelemetry": "9.7.0-alpha.0", + "@sentry/svelte": "9.7.0-alpha.0", "@sentry/vite-plugin": "3.2.0", "magic-string": "0.30.7", "recast": "0.23.11", diff --git a/packages/tanstackstart-react/package.json b/packages/tanstackstart-react/package.json index e19442ca0117..609151623410 100644 --- a/packages/tanstackstart-react/package.json +++ b/packages/tanstackstart-react/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/tanstackstart-react", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for TanStack Start React", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/tanstackstart-react", @@ -52,11 +52,11 @@ "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/semantic-conventions": "^1.30.0", - "@sentry-internal/browser-utils": "9.6.1", - "@sentry/core": "9.6.1", - "@sentry/node": "9.6.1", - "@sentry/opentelemetry": "9.6.1", - "@sentry/react": "9.6.1" + "@sentry-internal/browser-utils": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0", + "@sentry/node": "9.7.0-alpha.0", + "@sentry/opentelemetry": "9.7.0-alpha.0", + "@sentry/react": "9.7.0-alpha.0" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/tanstackstart/package.json b/packages/tanstackstart/package.json index 11341f3e3d96..5eebab08329f 100644 --- a/packages/tanstackstart/package.json +++ b/packages/tanstackstart/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/tanstackstart", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Utilities for the Sentry TanStack Start SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/tanstackstart", diff --git a/packages/types/package.json b/packages/types/package.json index 8061abb0369c..eb10b385201f 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/types", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Types for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types", @@ -56,7 +56,7 @@ "yalc:publish": "yalc publish --push --sig" }, "dependencies": { - "@sentry/core": "9.6.1" + "@sentry/core": "9.7.0-alpha.0" }, "volta": { "extends": "../../package.json" diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 8d983416cf19..146703d1ed37 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/typescript", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Typescript configuration used at Sentry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/typescript", diff --git a/packages/vercel-edge/package.json b/packages/vercel-edge/package.json index 1c53765ba6fa..9a7077dc300e 100644 --- a/packages/vercel-edge/package.json +++ b/packages/vercel-edge/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vercel-edge", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for the Vercel Edge Runtime", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vercel-edge", @@ -40,7 +40,7 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@sentry/core": "9.6.1" + "@sentry/core": "9.7.0-alpha.0" }, "devDependencies": { "@edge-runtime/types": "3.0.1", @@ -48,7 +48,7 @@ "@opentelemetry/resources": "^1.30.1", "@opentelemetry/sdk-trace-base": "^1.30.1", "@opentelemetry/semantic-conventions": "^1.28.0", - "@sentry/opentelemetry": "9.6.1" + "@sentry/opentelemetry": "9.7.0-alpha.0" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/vue/package.json b/packages/vue/package.json index 419bbeccb4ed..6ed97d14c5b1 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vue", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Official Sentry SDK for Vue.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vue", @@ -39,8 +39,8 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "9.6.1", - "@sentry/core": "9.6.1" + "@sentry/browser": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0" }, "peerDependencies": { "pinia": "2.x || 3.x", diff --git a/packages/wasm/package.json b/packages/wasm/package.json index ed67fb6b4a1d..4400a1858d4d 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/wasm", - "version": "9.6.1", + "version": "9.7.0-alpha.0", "description": "Support for WASM.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/wasm", @@ -39,8 +39,8 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "9.6.1", - "@sentry/core": "9.6.1" + "@sentry/browser": "9.7.0-alpha.0", + "@sentry/core": "9.7.0-alpha.0" }, "scripts": { "build": "run-p build:transpile build:bundle build:types",