diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 6004a046f126..f5d0364b7cda 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -321,10 +321,8 @@ jobs:
uses: ./.github/actions/restore-cache
env:
DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }}
- - name: Lint source files
+ - name: Run linter
run: yarn lint
- - name: Validate ES5 builds
- run: yarn validate:es5
job_circular_dep_check:
name: Circular Dependency Check
diff --git a/.github/workflows/flaky-test-detector.yml b/.github/workflows/flaky-test-detector.yml
index 9412da630775..bb414b553abd 100644
--- a/.github/workflows/flaky-test-detector.yml
+++ b/.github/workflows/flaky-test-detector.yml
@@ -77,5 +77,5 @@ jobs:
working-directory: packages/browser-integration-tests
env:
CHANGED_TEST_PATHS: ${{ steps.changed.outputs.browser_integration_files }}
- # Run 50 times when detecting changed test(s), else run all tests 5x
- TEST_RUN_COUNT: ${{ steps.changed.outputs.browser_integration == 'true' && 50 || 5 }}
+ # Run 100 times when detecting changed test(s), else run all tests 5x
+ TEST_RUN_COUNT: ${{ steps.changed.outputs.browser_integration == 'true' && 100 || 5 }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3d8824213ccd..19b48530e607 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,23 +4,6 @@
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
-## 7.45.0
-
-- build(cdn): Ensure ES5 bundles do not use non-ES5 code (#7550)
-- feat(core): Add trace function (#7556)
-- feat(hub): Make scope always defined on the hub (#7551)
-- feat(replay): Add `replay_id` to transaction DSC (#7571)
-- feat(replay): Capture fetch body size for replay events (#7524)
-- feat(sveltekit): Add performance monitoring for client load (#7537)
-- feat(sveltekit): Add performance monitoring to Sveltekit server handle (#7532)
-- feat(sveltekit): Add SvelteKit routing instrumentation (#7565)
-- fix(browser): Ensure keepalive flag is correctly set for parallel requests (#7553)
-- fix(core): Ensure `ignoreErrors` only applies to error events (#7573)
-- fix(node): Consider tracing error handler for process exit (#7558)
-- fix(otel): Make sure we use correct hub on finish (#7577)
-- fix(react): Handle case where error.cause already defined (#7557)
-- fix(tracing): Account for case where startTransaction returns undefined (#7566)
-
## 7.44.2
- fix(cdn): Fix ES5 CDN bundles (#7544)
diff --git a/package.json b/package.json
index e6a54d7d032c..2296a10dfede 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,6 @@
"link:yarn": "lerna exec yarn link",
"lint": "lerna run lint",
"lint:eslint": "lerna run lint:eslint",
- "validate:es5": "lerna run validate:es5",
"postpublish": "lerna run --stream --concurrency 1 postpublish",
"test": "lerna run --ignore @sentry-internal/* test",
"test:unit": "lerna run --ignore @sentry-internal/* test:unit",
@@ -90,7 +89,6 @@
"chai": "^4.1.2",
"codecov": "^3.6.5",
"deepmerge": "^4.2.2",
- "es-check": "7.1.0",
"eslint": "7.32.0",
"jest": "^27.5.1",
"jest-environment-node": "^27.5.1",
diff --git a/packages/browser-integration-tests/scripts/detectFlakyTests.ts b/packages/browser-integration-tests/scripts/detectFlakyTests.ts
index af0a5c86a18e..22977fa3ed83 100644
--- a/packages/browser-integration-tests/scripts/detectFlakyTests.ts
+++ b/packages/browser-integration-tests/scripts/detectFlakyTests.ts
@@ -6,72 +6,52 @@ import { promisify } from 'util';
const exec = promisify(childProcess.exec);
async function run(): Promise {
- let testPaths: string[] = [];
+ let testPaths = getTestPaths();
+ let failed = [];
- const changedPaths: string[] = process.env.CHANGED_TEST_PATHS ? JSON.parse(process.env.CHANGED_TEST_PATHS) : [];
+ try {
+ const changedPaths: string[] = process.env.CHANGED_TEST_PATHS ? JSON.parse(process.env.CHANGED_TEST_PATHS) : [];
- if (changedPaths.length > 0) {
- console.log(`Detected changed test paths:
+ if (changedPaths.length > 0) {
+ console.log(`Detected changed test paths:
${changedPaths.join('\n')}
`);
- testPaths = getTestPaths().filter(p => changedPaths.some(changedPath => changedPath.includes(p)));
- if (testPaths.length === 0) {
- console.log('Could not find matching tests, aborting...');
- process.exit(1);
+ testPaths = testPaths.filter(p => changedPaths.some(changedPath => changedPath.includes(p)));
}
+ } catch {
+ console.log('Could not detect changed test paths, running all tests.');
}
const cwd = path.join(__dirname, '../');
const runCount = parseInt(process.env.TEST_RUN_COUNT || '10');
- try {
- await new Promise((resolve, reject) => {
- const cp = childProcess.spawn(
- `yarn playwright test ${
- testPaths.length ? testPaths.join(' ') : './suites'
- } --browser='all' --reporter='line' --repeat-each ${runCount}`,
- { shell: true, cwd },
- );
-
- let error: Error | undefined;
-
- cp.stdout.on('data', data => {
- console.log(data ? (data as object).toString() : '');
- });
-
- cp.stderr.on('data', data => {
- console.log(data ? (data as object).toString() : '');
- });
-
- cp.on('error', e => {
- console.error(e);
- error = e;
- });
-
- cp.on('close', status => {
- const err = error || (status !== 0 ? new Error(`Process exited with status ${status}`) : undefined);
+ for (const testPath of testPaths) {
+ console.log(`Running test: ${testPath}`);
+ const start = Date.now();
- if (err) {
- reject(err);
- } else {
- resolve();
- }
+ try {
+ await exec(`yarn playwright test ${testPath} --browser='all' --repeat-each ${runCount}`, {
+ cwd,
});
- });
- } catch (error) {
- console.log('');
- console.log('');
-
- console.error(`⚠️ Some tests failed.`);
- console.error(error);
- process.exit(1);
+ const end = Date.now();
+ console.log(` ☑️ Passed ${runCount} times, avg. duration ${Math.ceil((end - start) / runCount)}ms`);
+ } catch (error) {
+ logError(error);
+ failed.push(testPath);
+ }
}
console.log('');
console.log('');
- console.log(`☑️ All tests passed.`);
+
+ if (failed.length > 0) {
+ console.error(`⚠️ ${failed.length} test(s) failed.`);
+ process.exit(1);
+ } else {
+ console.log(`☑️ ${testPaths.length} test(s) passed.`);
+ }
}
function getTestPaths(): string[] {
diff --git a/packages/browser-integration-tests/suites/replay/dsc/init.js b/packages/browser-integration-tests/suites/replay/dsc/init.js
deleted file mode 100644
index c43f001779eb..000000000000
--- a/packages/browser-integration-tests/suites/replay/dsc/init.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import * as Sentry from '@sentry/browser';
-import { Integrations } from '@sentry/tracing';
-
-window.Sentry = Sentry;
-window.Replay = new Sentry.Replay({
- flushMinDelay: 200,
- flushMaxDelay: 200,
- useCompression: false,
-});
-
-Sentry.init({
- dsn: 'https://public@dsn.ingest.sentry.io/1337',
- integrations: [new Integrations.BrowserTracing({ tracingOrigins: [/.*/] }), window.Replay],
- environment: 'production',
- tracesSampleRate: 1,
- replaysSessionSampleRate: 0.0,
- replaysOnErrorSampleRate: 1.0,
-});
-
-Sentry.configureScope(scope => {
- scope.setUser({ id: 'user123', segment: 'segmentB' });
- scope.setTransactionName('testTransactionDSC');
-});
diff --git a/packages/browser-integration-tests/suites/replay/dsc/test.ts b/packages/browser-integration-tests/suites/replay/dsc/test.ts
deleted file mode 100644
index 0819e9f7bf71..000000000000
--- a/packages/browser-integration-tests/suites/replay/dsc/test.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { expect } from '@playwright/test';
-import type { EventEnvelopeHeaders } from '@sentry/types';
-
-import { sentryTest } from '../../../utils/fixtures';
-import { envelopeHeaderRequestParser, getFirstSentryEnvelopeRequest } from '../../../utils/helpers';
-import { getReplaySnapshot, shouldSkipReplayTest, waitForReplayRunning } from '../../../utils/replayHelpers';
-
-sentryTest('should add replay_id to dsc of transactions', async ({ getLocalTestPath, page, browserName }) => {
- // This is flaky on webkit, so skipping there...
- if (shouldSkipReplayTest() || browserName === 'webkit') {
- sentryTest.skip();
- }
-
- const url = await getLocalTestPath({ testDir: __dirname });
- await page.goto(url);
-
- const envHeader = await getFirstSentryEnvelopeRequest(page, url, envelopeHeaderRequestParser);
-
- await waitForReplayRunning(page);
- const replay = await getReplaySnapshot(page);
-
- expect(replay.session?.id).toBeDefined();
-
- expect(envHeader.trace).toBeDefined();
- expect(envHeader.trace).toEqual({
- environment: 'production',
- user_segment: 'segmentB',
- sample_rate: '1',
- trace_id: expect.any(String),
- public_key: 'public',
- replay_id: replay.session?.id,
- });
-});
diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/contentLengthHeader/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/contentLengthHeader/test.ts
index 27c429c9be98..1ffeb360c650 100644
--- a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/contentLengthHeader/test.ts
+++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/contentLengthHeader/test.ts
@@ -2,11 +2,7 @@ import { expect } from '@playwright/test';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';
-import {
- getCustomRecordingEvents,
- shouldSkipReplayTest,
- waitForReplayRequest,
-} from '../../../../../utils/replayHelpers';
+import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers';
sentryTest('parses response_body_size from Content-Length header if available', async ({ getLocalTestPath, page }) => {
if (shouldSkipReplayTest()) {
@@ -26,17 +22,7 @@ sentryTest('parses response_body_size from Content-Length header if available',
});
});
- await page.route('https://dsn.ingest.sentry.io/**/*', route => {
- return route.fulfill({
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({ id: 'test-id' }),
- });
- });
-
const requestPromise = waitForErrorRequest(page);
- const replayRequestPromise1 = waitForReplayRequest(page, 0);
-
const url = await getLocalTestPath({ testDir: __dirname });
await page.goto(url);
@@ -72,20 +58,4 @@ sentryTest('parses response_body_size from Content-Length header if available',
url: 'http://localhost:7654/foo',
},
});
-
- const replayReq1 = await replayRequestPromise1;
- const { performanceSpans: performanceSpans1 } = getCustomRecordingEvents(replayReq1);
- expect(performanceSpans1.filter(span => span.op === 'resource.fetch')).toEqual([
- {
- data: {
- method: 'GET',
- responseBodySize: 789,
- statusCode: 200,
- },
- description: 'http://localhost:7654/foo',
- endTimestamp: expect.any(Number),
- op: 'resource.fetch',
- startTimestamp: expect.any(Number),
- },
- ]);
});
diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/noContentLengthHeader/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/noContentLengthHeader/test.ts
index 31f8d65bc7e7..8248b4799480 100644
--- a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/noContentLengthHeader/test.ts
+++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/noContentLengthHeader/test.ts
@@ -2,11 +2,7 @@ import { expect } from '@playwright/test';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';
-import {
- getCustomRecordingEvents,
- shouldSkipReplayTest,
- waitForReplayRequest,
-} from '../../../../../utils/replayHelpers';
+import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers';
sentryTest('does not capture response_body_size without Content-Length header', async ({ getLocalTestPath, page }) => {
if (shouldSkipReplayTest()) {
@@ -26,17 +22,7 @@ sentryTest('does not capture response_body_size without Content-Length header',
});
});
- await page.route('https://dsn.ingest.sentry.io/**/*', route => {
- return route.fulfill({
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({ id: 'test-id' }),
- });
- });
-
const requestPromise = waitForErrorRequest(page);
- const replayRequestPromise1 = waitForReplayRequest(page, 0);
-
const url = await getLocalTestPath({ testDir: __dirname });
await page.goto(url);
@@ -71,20 +57,4 @@ sentryTest('does not capture response_body_size without Content-Length header',
url: 'http://localhost:7654/foo',
},
});
-
- const replayReq1 = await replayRequestPromise1;
- const { performanceSpans: performanceSpans1 } = getCustomRecordingEvents(replayReq1);
- expect(performanceSpans1.filter(span => span.op === 'resource.fetch')).toEqual([
- {
- data: {
- method: 'GET',
- responseBodySize: 29,
- statusCode: 200,
- },
- description: 'http://localhost:7654/foo',
- endTimestamp: expect.any(Number),
- op: 'resource.fetch',
- startTimestamp: expect.any(Number),
- },
- ]);
});
diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/nonTextBody/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/nonTextBody/test.ts
index d2c167110a8a..a293df49b366 100644
--- a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/nonTextBody/test.ts
+++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/nonTextBody/test.ts
@@ -2,11 +2,7 @@ import { expect } from '@playwright/test';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';
-import {
- getCustomRecordingEvents,
- shouldSkipReplayTest,
- waitForReplayRequest,
-} from '../../../../../utils/replayHelpers';
+import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers';
sentryTest('calculates body sizes for non-string bodies', async ({ getLocalTestPath, page }) => {
if (shouldSkipReplayTest()) {
@@ -23,17 +19,7 @@ sentryTest('calculates body sizes for non-string bodies', async ({ getLocalTestP
});
});
- await page.route('https://dsn.ingest.sentry.io/**/*', route => {
- return route.fulfill({
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({ id: 'test-id' }),
- });
- });
-
const requestPromise = waitForErrorRequest(page);
- const replayRequestPromise1 = waitForReplayRequest(page, 0);
-
const url = await getLocalTestPath({ testDir: __dirname });
await page.goto(url);
@@ -74,21 +60,4 @@ sentryTest('calculates body sizes for non-string bodies', async ({ getLocalTestP
url: 'http://localhost:7654/foo',
},
});
-
- const replayReq1 = await replayRequestPromise1;
- const { performanceSpans: performanceSpans1 } = getCustomRecordingEvents(replayReq1);
- expect(performanceSpans1.filter(span => span.op === 'resource.fetch')).toEqual([
- {
- data: {
- method: 'POST',
- requestBodySize: 26,
- responseBodySize: 24,
- statusCode: 200,
- },
- description: 'http://localhost:7654/foo',
- endTimestamp: expect.any(Number),
- op: 'resource.fetch',
- startTimestamp: expect.any(Number),
- },
- ]);
});
diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/requestBody/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/requestBody/test.ts
index 0f77394b6e5d..baac9005fd35 100644
--- a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/requestBody/test.ts
+++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/fetch/requestBody/test.ts
@@ -2,11 +2,7 @@ import { expect } from '@playwright/test';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';
-import {
- getCustomRecordingEvents,
- shouldSkipReplayTest,
- waitForReplayRequest,
-} from '../../../../../utils/replayHelpers';
+import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers';
sentryTest('captures request_body_size when body is sent', async ({ getLocalTestPath, page }) => {
if (shouldSkipReplayTest()) {
@@ -16,23 +12,16 @@ sentryTest('captures request_body_size when body is sent', async ({ getLocalTest
await page.route('**/foo', route => {
return route.fulfill({
status: 200,
+ body: JSON.stringify({
+ userNames: ['John', 'Jane'],
+ }),
headers: {
'Content-Type': 'application/json',
},
});
});
- await page.route('https://dsn.ingest.sentry.io/**/*', route => {
- return route.fulfill({
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({ id: 'test-id' }),
- });
- });
-
const requestPromise = waitForErrorRequest(page);
- const replayRequestPromise1 = waitForReplayRequest(page, 0);
-
const url = await getLocalTestPath({ testDir: __dirname });
await page.goto(url);
@@ -59,32 +48,5 @@ sentryTest('captures request_body_size when body is sent', async ({ getLocalTest
expect(eventData.exception?.values).toHaveLength(1);
expect(eventData?.breadcrumbs?.length).toBe(1);
- expect(eventData!.breadcrumbs![0]).toEqual({
- timestamp: expect.any(Number),
- category: 'fetch',
- type: 'http',
- data: {
- method: 'POST',
- request_body_size: 13,
- status_code: 200,
- url: 'http://localhost:7654/foo',
- },
- });
expect(eventData!.breadcrumbs![0].data!.request_body_size).toEqual(13);
-
- const replayReq1 = await replayRequestPromise1;
- const { performanceSpans: performanceSpans1 } = getCustomRecordingEvents(replayReq1);
- expect(performanceSpans1.filter(span => span.op === 'resource.fetch')).toEqual([
- {
- data: {
- method: 'POST',
- requestBodySize: 13,
- statusCode: 200,
- },
- description: 'http://localhost:7654/foo',
- endTimestamp: expect.any(Number),
- op: 'resource.fetch',
- startTimestamp: expect.any(Number),
- },
- ]);
});
diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/contentLengthHeader/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/contentLengthHeader/test.ts
index 4ee170939530..b5f517f77352 100644
--- a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/contentLengthHeader/test.ts
+++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/contentLengthHeader/test.ts
@@ -2,11 +2,7 @@ import { expect } from '@playwright/test';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';
-import {
- getCustomRecordingEvents,
- shouldSkipReplayTest,
- waitForReplayRequest,
-} from '../../../../../utils/replayHelpers';
+import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers';
sentryTest(
'parses response_body_size from Content-Length header if available',
@@ -29,17 +25,7 @@ sentryTest(
});
});
- await page.route('https://dsn.ingest.sentry.io/**/*', route => {
- return route.fulfill({
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({ id: 'test-id' }),
- });
- });
-
const requestPromise = waitForErrorRequest(page);
- const replayRequestPromise1 = waitForReplayRequest(page, 0);
-
const url = await getLocalTestPath({ testDir: __dirname });
await page.goto(url);
@@ -79,21 +65,5 @@ sentryTest(
url: 'http://localhost:7654/foo',
},
});
-
- const replayReq1 = await replayRequestPromise1;
- const { performanceSpans: performanceSpans1 } = getCustomRecordingEvents(replayReq1);
- expect(performanceSpans1.filter(span => span.op === 'resource.xhr')).toEqual([
- {
- data: {
- method: 'GET',
- responseBodySize: 789,
- statusCode: 200,
- },
- description: 'http://localhost:7654/foo',
- endTimestamp: expect.any(Number),
- op: 'resource.xhr',
- startTimestamp: expect.any(Number),
- },
- ]);
},
);
diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/noContentLengthHeader/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/noContentLengthHeader/test.ts
index 9a9bd633c71f..9ea10831afab 100644
--- a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/noContentLengthHeader/test.ts
+++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/noContentLengthHeader/test.ts
@@ -2,11 +2,7 @@ import { expect } from '@playwright/test';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';
-import {
- getCustomRecordingEvents,
- shouldSkipReplayTest,
- waitForReplayRequest,
-} from '../../../../../utils/replayHelpers';
+import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers';
sentryTest(
'captures response_body_size without Content-Length header',
@@ -29,17 +25,7 @@ sentryTest(
});
});
- await page.route('https://dsn.ingest.sentry.io/**/*', route => {
- return route.fulfill({
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({ id: 'test-id' }),
- });
- });
-
const requestPromise = waitForErrorRequest(page);
- const replayRequestPromise1 = waitForReplayRequest(page, 0);
-
const url = await getLocalTestPath({ testDir: __dirname });
await page.goto(url);
@@ -79,21 +65,5 @@ sentryTest(
url: 'http://localhost:7654/foo',
},
});
-
- const replayReq1 = await replayRequestPromise1;
- const { performanceSpans: performanceSpans1 } = getCustomRecordingEvents(replayReq1);
- expect(performanceSpans1.filter(span => span.op === 'resource.xhr')).toEqual([
- {
- data: {
- method: 'GET',
- responseBodySize: 29,
- statusCode: 200,
- },
- description: 'http://localhost:7654/foo',
- endTimestamp: expect.any(Number),
- op: 'resource.xhr',
- startTimestamp: expect.any(Number),
- },
- ]);
},
);
diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/nonTextBody/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/nonTextBody/test.ts
index 0210283fea60..5142f2e6be82 100644
--- a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/nonTextBody/test.ts
+++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/nonTextBody/test.ts
@@ -2,11 +2,7 @@ import { expect } from '@playwright/test';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';
-import {
- getCustomRecordingEvents,
- shouldSkipReplayTest,
- waitForReplayRequest,
-} from '../../../../../utils/replayHelpers';
+import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers';
sentryTest('calculates body sizes for non-string bodies', async ({ getLocalTestPath, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
@@ -24,17 +20,7 @@ sentryTest('calculates body sizes for non-string bodies', async ({ getLocalTestP
});
});
- await page.route('https://dsn.ingest.sentry.io/**/*', route => {
- return route.fulfill({
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({ id: 'test-id' }),
- });
- });
-
const requestPromise = waitForErrorRequest(page);
- const replayRequestPromise1 = waitForReplayRequest(page, 0);
-
const url = await getLocalTestPath({ testDir: __dirname });
await page.goto(url);
@@ -77,21 +63,4 @@ sentryTest('calculates body sizes for non-string bodies', async ({ getLocalTestP
url: 'http://localhost:7654/foo',
},
});
-
- const replayReq1 = await replayRequestPromise1;
- const { performanceSpans: performanceSpans1 } = getCustomRecordingEvents(replayReq1);
- expect(performanceSpans1.filter(span => span.op === 'resource.xhr')).toEqual([
- {
- data: {
- method: 'POST',
- requestBodySize: 26,
- responseBodySize: 24,
- statusCode: 200,
- },
- description: 'http://localhost:7654/foo',
- endTimestamp: expect.any(Number),
- op: 'resource.xhr',
- startTimestamp: expect.any(Number),
- },
- ]);
});
diff --git a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/requestBody/test.ts b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/requestBody/test.ts
index 470fe57c51ba..fd3cc426f9fd 100644
--- a/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/requestBody/test.ts
+++ b/packages/browser-integration-tests/suites/replay/extendNetworkBreadcrumbs/xhr/requestBody/test.ts
@@ -2,11 +2,7 @@ import { expect } from '@playwright/test';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../../utils/helpers';
-import {
- getCustomRecordingEvents,
- shouldSkipReplayTest,
- waitForReplayRequest,
-} from '../../../../../utils/replayHelpers';
+import { shouldSkipReplayTest } from '../../../../../utils/replayHelpers';
sentryTest('captures request_body_size when body is sent', async ({ getLocalTestPath, page, browserName }) => {
// These are a bit flaky on non-chromium browsers
@@ -17,6 +13,9 @@ sentryTest('captures request_body_size when body is sent', async ({ getLocalTest
await page.route('**/foo', route => {
return route.fulfill({
status: 200,
+ body: JSON.stringify({
+ userNames: ['John', 'Jane'],
+ }),
headers: {
'Content-Type': 'application/json',
'Content-Length': '',
@@ -24,17 +23,7 @@ sentryTest('captures request_body_size when body is sent', async ({ getLocalTest
});
});
- await page.route('https://dsn.ingest.sentry.io/**/*', route => {
- return route.fulfill({
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({ id: 'test-id' }),
- });
- });
-
const requestPromise = waitForErrorRequest(page);
- const replayRequestPromise1 = waitForReplayRequest(page, 0);
-
const url = await getLocalTestPath({ testDir: __dirname });
await page.goto(url);
@@ -63,31 +52,5 @@ sentryTest('captures request_body_size when body is sent', async ({ getLocalTest
expect(eventData.exception?.values).toHaveLength(1);
expect(eventData?.breadcrumbs?.length).toBe(1);
- expect(eventData!.breadcrumbs![0]).toEqual({
- timestamp: expect.any(Number),
- category: 'xhr',
- type: 'http',
- data: {
- method: 'POST',
- request_body_size: 13,
- status_code: 200,
- url: 'http://localhost:7654/foo',
- },
- });
-
- const replayReq1 = await replayRequestPromise1;
- const { performanceSpans: performanceSpans1 } = getCustomRecordingEvents(replayReq1);
- expect(performanceSpans1.filter(span => span.op === 'resource.xhr')).toEqual([
- {
- data: {
- method: 'POST',
- requestBodySize: 13,
- statusCode: 200,
- },
- description: 'http://localhost:7654/foo',
- endTimestamp: expect.any(Number),
- op: 'resource.xhr',
- startTimestamp: expect.any(Number),
- },
- ]);
+ expect(eventData!.breadcrumbs![0].data!.request_body_size).toEqual(13);
});
diff --git a/packages/browser-integration-tests/suites/replay/privacyInput/test.ts b/packages/browser-integration-tests/suites/replay/privacyInput/test.ts
index dc071b9bf487..f95e857d5637 100644
--- a/packages/browser-integration-tests/suites/replay/privacyInput/test.ts
+++ b/packages/browser-integration-tests/suites/replay/privacyInput/test.ts
@@ -24,34 +24,10 @@ sentryTest(
sentryTest.skip();
}
- // We want to ensure to check the correct event payloads
- const inputMutationSegmentIds: number[] = [];
const reqPromise0 = waitForReplayRequest(page, 0);
- const reqPromise1 = waitForReplayRequest(page, (event, res) => {
- const check = inputMutationSegmentIds.length === 0 && getIncrementalRecordingSnapshots(res).some(isInputMutation);
-
- if (check) {
- inputMutationSegmentIds.push(event.segment_id);
- }
-
- return check;
- });
- const reqPromise2 = waitForReplayRequest(page, (event, res) => {
- const check =
- inputMutationSegmentIds.length === 1 &&
- inputMutationSegmentIds[0] < event.segment_id &&
- getIncrementalRecordingSnapshots(res).some(isInputMutation);
-
- if (check) {
- inputMutationSegmentIds.push(event.segment_id);
- }
-
- return check;
- });
- const reqPromise3 = waitForReplayRequest(page, event => {
- // This one should not have any input mutations
- return inputMutationSegmentIds.length === 2 && inputMutationSegmentIds[1] < event.segment_id;
- });
+ const reqPromise1 = waitForReplayRequest(page, 1);
+ const reqPromise2 = waitForReplayRequest(page, 2);
+ const reqPromise3 = waitForReplayRequest(page, 3);
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
return route.fulfill({
@@ -96,34 +72,10 @@ sentryTest(
sentryTest.skip();
}
- // We want to ensure to check the correct event payloads
- const inputMutationSegmentIds: number[] = [];
const reqPromise0 = waitForReplayRequest(page, 0);
- const reqPromise1 = waitForReplayRequest(page, (event, res) => {
- const check = inputMutationSegmentIds.length === 0 && getIncrementalRecordingSnapshots(res).some(isInputMutation);
-
- if (check) {
- inputMutationSegmentIds.push(event.segment_id);
- }
-
- return check;
- });
- const reqPromise2 = waitForReplayRequest(page, (event, res) => {
- const check =
- inputMutationSegmentIds.length === 1 &&
- inputMutationSegmentIds[0] < event.segment_id &&
- getIncrementalRecordingSnapshots(res).some(isInputMutation);
-
- if (check) {
- inputMutationSegmentIds.push(event.segment_id);
- }
-
- return check;
- });
- const reqPromise3 = waitForReplayRequest(page, event => {
- // This one should not have any input mutations
- return inputMutationSegmentIds.length === 2 && inputMutationSegmentIds[1] < event.segment_id;
- });
+ const reqPromise1 = waitForReplayRequest(page, 1);
+ const reqPromise2 = waitForReplayRequest(page, 2);
+ const reqPromise3 = waitForReplayRequest(page, 3);
await page.route('https://dsn.ingest.sentry.io/**/*', route => {
return route.fulfill({
diff --git a/packages/browser-integration-tests/utils/replayHelpers.ts b/packages/browser-integration-tests/utils/replayHelpers.ts
index bd7696ebd927..cf21ce7b9c7b 100644
--- a/packages/browser-integration-tests/utils/replayHelpers.ts
+++ b/packages/browser-integration-tests/utils/replayHelpers.ts
@@ -99,16 +99,6 @@ function isCustomSnapshot(event: RecordingEvent): event is RecordingEvent & { da
return event.type === EventType.Custom;
}
-/** Wait for replay to be running & available. */
-export async function waitForReplayRunning(page: Page): Promise {
- await page.waitForFunction(() => {
- const replayIntegration = (window as unknown as Window & { Replay: { _replay: ReplayContainer } }).Replay;
- const replay = replayIntegration._replay;
-
- return replay.isEnabled() && replay.session?.id !== undefined;
- });
-}
-
/**
* This returns the replay container (assuming it exists).
* Note that due to how this works with playwright, this is a POJO copy of replay.
diff --git a/packages/browser/package.json b/packages/browser/package.json
index fb2162449405..5d93e763b309 100644
--- a/packages/browser/package.json
+++ b/packages/browser/package.json
@@ -66,7 +66,6 @@
"lint": "run-s lint:prettier lint:eslint",
"lint:eslint": "eslint . --format stylish",
"lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"",
- "validate:es5": "es-check es5 build/bundles/bundle.es5.js",
"size:check": "run-p size:check:es5 size:check:es6",
"size:check:es5": "cat build/bundles/bundle.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES5: \",$1,\"kB\";}'",
"size:check:es6": "cat build/bundles/bundle.es6.min.js | gzip -9 | wc -c | awk '{$1=$1/1024; print \"ES6: \",$1,\"kB\";}'",
diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts
index 1d0bd091cf19..8cafc77a68c3 100644
--- a/packages/browser/src/client.ts
+++ b/packages/browser/src/client.ts
@@ -1,5 +1,5 @@
import type { Scope } from '@sentry/core';
-import { BaseClient, SDK_VERSION } from '@sentry/core';
+import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, SDK_VERSION } from '@sentry/core';
import type {
BrowserClientReplayOptions,
ClientOptions,
@@ -9,7 +9,7 @@ import type {
Severity,
SeverityLevel,
} from '@sentry/types';
-import { createClientReportEnvelope, dsnToString, getSDKSource, logger } from '@sentry/utils';
+import { createClientReportEnvelope, dsnToString, getSDKSource, logger, serializeEnvelope } from '@sentry/utils';
import { eventFromException, eventFromMessage } from './eventbuilder';
import { WINDOW } from './helpers';
@@ -132,7 +132,24 @@ export class BrowserClient extends BaseClient {
__DEBUG_BUILD__ && logger.log('Sending outcomes:', outcomes);
+ const url = getEnvelopeEndpointWithUrlEncodedAuth(this._dsn, this._options);
const envelope = createClientReportEnvelope(outcomes, this._options.tunnel && dsnToString(this._dsn));
- void this._sendEnvelope(envelope);
+
+ try {
+ const isRealNavigator = Object.prototype.toString.call(WINDOW && WINDOW.navigator) === '[object Navigator]';
+ const hasSendBeacon = isRealNavigator && typeof WINDOW.navigator.sendBeacon === 'function';
+ // Make sure beacon is not used if user configures custom transport options
+ if (hasSendBeacon && !this._options.transportOptions) {
+ // Prevent illegal invocations - https://xgwang.me/posts/you-may-not-know-beacon/#it-may-throw-error%2C-be-sure-to-catch
+ const sendBeacon = WINDOW.navigator.sendBeacon.bind(WINDOW.navigator);
+ sendBeacon(url, serializeEnvelope(envelope));
+ } else {
+ // If beacon is not supported or if they are using the tunnel option
+ // use our regular transport to send client reports to Sentry.
+ void this._sendEnvelope(envelope);
+ }
+ } catch (e) {
+ __DEBUG_BUILD__ && logger.error(e);
+ }
}
}
diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts
index dca32f05b35d..59d814ac9412 100644
--- a/packages/browser/src/index.ts
+++ b/packages/browser/src/index.ts
@@ -21,8 +21,8 @@ const INTEGRATIONS = {
export { INTEGRATIONS as Integrations };
export { Replay } from '@sentry/replay';
-export { BrowserTracing, defaultRequestInstrumentationOptions } from '@sentry-internal/tracing';
-export { addTracingExtensions, getActiveTransaction } from '@sentry/core';
+export { BrowserTracing } from '@sentry-internal/tracing';
+export { addTracingExtensions } from '@sentry/core';
export { makeBrowserOfflineTransport } from './transports/offline';
export { onProfilingStartRouteTransaction } from './profiling/hubextensions';
export { BrowserProfilingIntegration } from './profiling/integration';
diff --git a/packages/browser/src/transports/fetch.ts b/packages/browser/src/transports/fetch.ts
index e996b6f8277a..83b75c82ba39 100644
--- a/packages/browser/src/transports/fetch.ts
+++ b/packages/browser/src/transports/fetch.ts
@@ -13,14 +13,7 @@ export function makeFetchTransport(
options: BrowserTransportOptions,
nativeFetch: FetchImpl = getNativeFetchImplementation(),
): Transport {
- let pendingBodySize = 0;
- let pendingCount = 0;
-
function makeRequest(request: TransportRequest): PromiseLike {
- const requestSize = request.body.length;
- pendingBodySize += requestSize;
- pendingCount++;
-
const requestOptions: RequestInit = {
body: request.body,
method: 'POST',
@@ -32,31 +25,23 @@ export function makeFetchTransport(
// frequently sending events right before the user is switching pages (eg. whenfinishing navigation transactions).
// Gotchas:
// - `keepalive` isn't supported by Firefox
- // - As per spec (https://fetch.spec.whatwg.org/#http-network-or-cache-fetch):
- // If the sum of contentLength and inflightKeepaliveBytes is greater than 64 kibibytes, then return a network error.
- // We will therefore only activate the flag when we're below that limit.
- // There is also a limit of requests that can be open at the same time, so we also limit this to 15
- // See https://github.com/getsentry/sentry-javascript/pull/7553 for details
- keepalive: pendingBodySize <= 60_000 && pendingCount < 15,
+ // - As per spec (https://fetch.spec.whatwg.org/#http-network-or-cache-fetch), a request with `keepalive: true`
+ // and a content length of > 64 kibibytes returns a network error. We will therefore only activate the flag when
+ // we're below that limit.
+ keepalive: request.body.length <= 65536,
...options.fetchOptions,
};
try {
- return nativeFetch(options.url, requestOptions).then(response => {
- pendingBodySize -= requestSize;
- pendingCount--;
- return {
- statusCode: response.status,
- headers: {
- 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'),
- 'retry-after': response.headers.get('Retry-After'),
- },
- };
- });
+ return nativeFetch(options.url, requestOptions).then(response => ({
+ statusCode: response.status,
+ headers: {
+ 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'),
+ 'retry-after': response.headers.get('Retry-After'),
+ },
+ }));
} catch (e) {
clearCachedFetchImplementation();
- pendingBodySize -= requestSize;
- pendingCount--;
return rejectedSyncPromise(e);
}
}
diff --git a/packages/browser/test/unit/transports/fetch.test.ts b/packages/browser/test/unit/transports/fetch.test.ts
index e6b58eeaa110..53ed5c34e21b 100644
--- a/packages/browser/test/unit/transports/fetch.test.ts
+++ b/packages/browser/test/unit/transports/fetch.test.ts
@@ -16,11 +16,6 @@ const ERROR_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4b
[{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem,
]);
-const LARGE_ERROR_ENVELOPE = createEnvelope(
- { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' },
- [[{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', message: 'x'.repeat(10 * 900) }] as EventItem],
-);
-
class Headers {
headers: { [key: string]: string } = {};
get(key: string) {
@@ -112,54 +107,4 @@ describe('NewFetchTransport', () => {
await expect(() => transport.send(ERROR_ENVELOPE)).not.toThrow();
expect(mockFetch).toHaveBeenCalledTimes(1);
});
-
- it('correctly sets keepalive flag', async () => {
- const mockFetch = jest.fn(() =>
- Promise.resolve({
- headers: new Headers(),
- status: 200,
- text: () => Promise.resolve({}),
- }),
- ) as unknown as FetchImpl;
-
- const REQUEST_OPTIONS: RequestInit = {
- referrerPolicy: 'strict-origin',
- referrer: 'http://example.org',
- };
-
- const transport = makeFetchTransport(
- { ...DEFAULT_FETCH_TRANSPORT_OPTIONS, fetchOptions: REQUEST_OPTIONS },
- mockFetch,
- );
-
- const promises: PromiseLike[] = [];
- for (let i = 0; i < 30; i++) {
- promises.push(transport.send(LARGE_ERROR_ENVELOPE));
- }
-
- await Promise.all(promises);
-
- for (let i = 1; i <= 30; i++) {
- // After 7 requests, we hit the total limit of >64kb of size
- // Starting there, keepalive should be false
- const keepalive = i < 7;
- expect(mockFetch).toHaveBeenNthCalledWith(i, expect.any(String), expect.objectContaining({ keepalive }));
- }
-
- (mockFetch as jest.Mock).mockClear();
-
- // Limit resets when requests have resolved
- // Now try based on # of pending requests
- const promises2 = [];
- for (let i = 0; i < 20; i++) {
- promises2.push(transport.send(ERROR_ENVELOPE));
- }
-
- await Promise.all(promises2);
-
- for (let i = 1; i <= 20; i++) {
- const keepalive = i < 15;
- expect(mockFetch).toHaveBeenNthCalledWith(i, expect.any(String), expect.objectContaining({ keepalive }));
- }
- });
});
diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts
index 80be9dc037bd..a6db2788aa52 100644
--- a/packages/core/src/baseclient.ts
+++ b/packages/core/src/baseclient.ts
@@ -6,7 +6,6 @@ import type {
ClientOptions,
DataCategory,
DsnComponents,
- DynamicSamplingContext,
Envelope,
ErrorEvent,
Event,
@@ -379,9 +378,6 @@ export abstract class BaseClient implements Client {
/** @inheritdoc */
public on(hook: 'beforeAddBreadcrumb', callback: (breadcrumb: Breadcrumb, hint?: BreadcrumbHint) => void): void;
- /** @inheritdoc */
- public on(hook: 'createDsc', callback: (dsc: DynamicSamplingContext) => void): void;
-
/** @inheritdoc */
public on(hook: string, callback: unknown): void {
if (!this._hooks[hook]) {
@@ -404,9 +400,6 @@ export abstract class BaseClient implements Client {
/** @inheritdoc */
public emit(hook: 'beforeAddBreadcrumb', breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void;
- /** @inheritdoc */
- public emit(hook: 'createDsc', dsc: DynamicSamplingContext): void;
-
/** @inheritdoc */
public emit(hook: string, ...rest: unknown[]): void {
if (this._hooks[hook]) {
diff --git a/packages/core/src/hub.ts b/packages/core/src/hub.ts
index f67ae3773325..203241a35389 100644
--- a/packages/core/src/hub.ts
+++ b/packages/core/src/hub.ts
@@ -56,7 +56,7 @@ const DEFAULT_BREADCRUMBS = 100;
*/
export interface Layer {
client?: Client;
- scope: Scope;
+ scope?: Scope;
}
/**
@@ -87,7 +87,7 @@ export interface Carrier {
*/
export class Hub implements HubInterface {
/** Is a {@link Layer}[] containing the client and scope */
- private readonly _stack: Layer[];
+ private readonly _stack: Layer[] = [{}];
/** Contains the last event id of a captured event. */
private _lastEventId?: string;
@@ -101,7 +101,7 @@ export class Hub implements HubInterface {
* @param version number, higher number means higher priority.
*/
public constructor(client?: Client, scope: Scope = new Scope(), private readonly _version: number = API_VERSION) {
- this._stack = [{ scope }];
+ this.getStackTop().scope = scope;
if (client) {
this.bindClient(client);
}
@@ -166,7 +166,7 @@ export class Hub implements HubInterface {
}
/** Returns the scope of the top stack. */
- public getScope(): Scope {
+ public getScope(): Scope | undefined {
return this.getStackTop().scope;
}
@@ -256,7 +256,7 @@ export class Hub implements HubInterface {
public addBreadcrumb(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void {
const { scope, client } = this.getStackTop();
- if (!client) return;
+ if (!scope || !client) return;
const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS } =
(client.getOptions && client.getOptions()) || {};
@@ -282,35 +282,40 @@ export class Hub implements HubInterface {
* @inheritDoc
*/
public setUser(user: User | null): void {
- this.getScope().setUser(user);
+ const scope = this.getScope();
+ if (scope) scope.setUser(user);
}
/**
* @inheritDoc
*/
public setTags(tags: { [key: string]: Primitive }): void {
- this.getScope().setTags(tags);
+ const scope = this.getScope();
+ if (scope) scope.setTags(tags);
}
/**
* @inheritDoc
*/
public setExtras(extras: Extras): void {
- this.getScope().setExtras(extras);
+ const scope = this.getScope();
+ if (scope) scope.setExtras(extras);
}
/**
* @inheritDoc
*/
public setTag(key: string, value: Primitive): void {
- this.getScope().setTag(key, value);
+ const scope = this.getScope();
+ if (scope) scope.setTag(key, value);
}
/**
* @inheritDoc
*/
public setExtra(key: string, extra: Extra): void {
- this.getScope().setExtra(key, extra);
+ const scope = this.getScope();
+ if (scope) scope.setExtra(key, extra);
}
/**
@@ -318,7 +323,8 @@ export class Hub implements HubInterface {
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public setContext(name: string, context: { [key: string]: any } | null): void {
- this.getScope().setContext(name, context);
+ const scope = this.getScope();
+ if (scope) scope.setContext(name, context);
}
/**
@@ -326,7 +332,7 @@ export class Hub implements HubInterface {
*/
public configureScope(callback: (scope: Scope) => void): void {
const { scope, client } = this.getStackTop();
- if (client) {
+ if (scope && client) {
callback(scope);
}
}
@@ -389,15 +395,17 @@ export class Hub implements HubInterface {
*/
public endSession(): void {
const layer = this.getStackTop();
- const scope = layer.scope;
- const session = scope.getSession();
+ const scope = layer && layer.scope;
+ const session = scope && scope.getSession();
if (session) {
closeSession(session);
}
this._sendSessionUpdate();
// the session is over; take it off of the scope
- scope.setSession();
+ if (scope) {
+ scope.setSession();
+ }
}
/**
@@ -413,20 +421,22 @@ export class Hub implements HubInterface {
const session = makeSession({
release,
environment,
- user: scope.getUser(),
+ ...(scope && { user: scope.getUser() }),
...(userAgent && { userAgent }),
...context,
});
- // End existing session if there's one
- const currentSession = scope.getSession && scope.getSession();
- if (currentSession && currentSession.status === 'ok') {
- updateSession(currentSession, { status: 'exited' });
- }
- this.endSession();
+ if (scope) {
+ // End existing session if there's one
+ const currentSession = scope.getSession && scope.getSession();
+ if (currentSession && currentSession.status === 'ok') {
+ updateSession(currentSession, { status: 'exited' });
+ }
+ this.endSession();
- // Afterwards we set the new session on the scope
- scope.setSession(session);
+ // Afterwards we set the new session on the scope
+ scope.setSession(session);
+ }
return session;
}
@@ -462,7 +472,7 @@ export class Hub implements HubInterface {
* @param method The method to call on the client.
* @param args Arguments to pass to the client function.
*/
- private _withClient(callback: (client: Client, scope: Scope) => void): void {
+ private _withClient(callback: (client: Client, scope: Scope | undefined) => void): void {
const { scope, client } = this.getStackTop();
if (client) {
callback(client, scope);
diff --git a/packages/core/src/integrations/inboundfilters.ts b/packages/core/src/integrations/inboundfilters.ts
index e790e3daf4b5..047060ae4961 100644
--- a/packages/core/src/integrations/inboundfilters.ts
+++ b/packages/core/src/integrations/inboundfilters.ts
@@ -103,8 +103,7 @@ export function _shouldDropEvent(event: Event, options: Partial): boolean {
- // If event.type, this is not an error
- if (event.type || !ignoreErrors || !ignoreErrors.length) {
+ if (!ignoreErrors || !ignoreErrors.length) {
return false;
}
diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts
index 5dc58a5fc944..7d2df2115ddd 100644
--- a/packages/core/src/sdk.ts
+++ b/packages/core/src/sdk.ts
@@ -28,7 +28,9 @@ export function initAndBind(
}
const hub = getCurrentHub();
const scope = hub.getScope();
- scope.update(options.initialScope);
+ if (scope) {
+ scope.update(options.initialScope);
+ }
const client = new clientClass(options);
hub.bindClient(client);
diff --git a/packages/core/src/sessionflusher.ts b/packages/core/src/sessionflusher.ts
index 4ef196819504..9b4579ade486 100644
--- a/packages/core/src/sessionflusher.ts
+++ b/packages/core/src/sessionflusher.ts
@@ -72,13 +72,15 @@ export class SessionFlusher implements SessionFlusherLike {
return;
}
const scope = getCurrentHub().getScope();
- const requestSession = scope.getRequestSession();
+ const requestSession = scope && scope.getRequestSession();
if (requestSession && requestSession.status) {
this._incrementSessionStatusCount(requestSession.status, new Date());
// This is not entirely necessarily but is added as a safe guard to indicate the bounds of a request and so in
// case captureRequestSession is called more than once to prevent double count
- scope.setRequestSession(undefined);
+ if (scope) {
+ scope.setRequestSession(undefined);
+ }
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
}
}
diff --git a/packages/core/src/tracing/errors.ts b/packages/core/src/tracing/errors.ts
index 8785bd86d448..3351f428fecf 100644
--- a/packages/core/src/tracing/errors.ts
+++ b/packages/core/src/tracing/errors.ts
@@ -29,7 +29,3 @@ function errorCallback(): void {
activeTransaction.setStatus(status);
}
}
-
-// The function name will be lost when bundling but we need to be able to identify this listener later to maintain the
-// node.js default exit behaviour
-errorCallback.tag = 'sentry_tracingErrorCallback';
diff --git a/packages/core/src/tracing/hubextensions.ts b/packages/core/src/tracing/hubextensions.ts
index ad6858d41e4a..b5518f95e04b 100644
--- a/packages/core/src/tracing/hubextensions.ts
+++ b/packages/core/src/tracing/hubextensions.ts
@@ -11,13 +11,15 @@ import { Transaction } from './transaction';
/** Returns all trace headers that are currently on the top scope. */
function traceHeaders(this: Hub): { [key: string]: string } {
const scope = this.getScope();
- const span = scope.getSpan();
-
- return span
- ? {
+ if (scope) {
+ const span = scope.getSpan();
+ if (span) {
+ return {
'sentry-trace': span.toTraceparent(),
- }
- : {};
+ };
+ }
+ }
+ return {};
}
/**
diff --git a/packages/core/src/tracing/idletransaction.ts b/packages/core/src/tracing/idletransaction.ts
index 77b7d5317da1..f4604a312519 100644
--- a/packages/core/src/tracing/idletransaction.ts
+++ b/packages/core/src/tracing/idletransaction.ts
@@ -346,7 +346,10 @@ export class IdleTransaction extends Transaction {
*/
function clearActiveTransaction(hub: Hub): void {
const scope = hub.getScope();
- if (scope.getTransaction()) {
- scope.setSpan(undefined);
+ if (scope) {
+ const transaction = scope.getTransaction();
+ if (transaction) {
+ scope.setSpan(undefined);
+ }
}
}
diff --git a/packages/core/src/tracing/index.ts b/packages/core/src/tracing/index.ts
index 1afb556bce4d..fd4949257ceb 100644
--- a/packages/core/src/tracing/index.ts
+++ b/packages/core/src/tracing/index.ts
@@ -6,4 +6,3 @@ export { extractTraceparentData, getActiveTransaction, stripUrlQueryAndFragment,
// eslint-disable-next-line deprecation/deprecation
export { SpanStatus } from './spanstatus';
export type { SpanStatusType } from './span';
-export { trace } from './trace';
diff --git a/packages/core/src/tracing/trace.ts b/packages/core/src/tracing/trace.ts
deleted file mode 100644
index 8e7844d23988..000000000000
--- a/packages/core/src/tracing/trace.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import type { TransactionContext } from '@sentry/types';
-import { isThenable } from '@sentry/utils';
-
-import { getCurrentHub } from '../hub';
-import type { Span } from './span';
-
-/**
- * Wraps a function with a transaction/span and finishes the span after the function is done.
- *
- * Note that if you have not enabled tracing extensions via `addTracingExtensions`, this function
- * will not generate spans, and the `span` returned from the callback may be undefined.
- *
- * This function is meant to be used internally and may break at any time. Use at your own risk.
- *
- * @internal
- * @private
- */
-export function trace(
- context: TransactionContext,
- callback: (span?: Span) => T,
- // eslint-disable-next-line @typescript-eslint/no-empty-function
- onError: (error: unknown) => void = () => {},
-): T {
- const ctx = { ...context };
- // If a name is set and a description is not, set the description to the name.
- if (ctx.name !== undefined && ctx.description === undefined) {
- ctx.description = ctx.name;
- }
-
- const hub = getCurrentHub();
- const scope = hub.getScope();
-
- const parentSpan = scope.getSpan();
- const activeSpan = parentSpan ? parentSpan.startChild(ctx) : hub.startTransaction(ctx);
- scope.setSpan(activeSpan);
-
- function finishAndSetSpan(): void {
- activeSpan && activeSpan.finish();
- hub.getScope().setSpan(parentSpan);
- }
-
- let maybePromiseResult: T;
- try {
- maybePromiseResult = callback(activeSpan);
- } catch (e) {
- activeSpan && activeSpan.setStatus('internal_error');
- onError(e);
- finishAndSetSpan();
- throw e;
- }
-
- if (isThenable(maybePromiseResult)) {
- Promise.resolve(maybePromiseResult).then(
- () => {
- finishAndSetSpan();
- },
- e => {
- activeSpan && activeSpan.setStatus('internal_error');
- onError(e);
- finishAndSetSpan();
- },
- );
- } else {
- finishAndSetSpan();
- }
-
- return maybePromiseResult;
-}
diff --git a/packages/core/src/tracing/transaction.ts b/packages/core/src/tracing/transaction.ts
index 9dcee22fb888..eba498b7e654 100644
--- a/packages/core/src/tracing/transaction.ts
+++ b/packages/core/src/tracing/transaction.ts
@@ -256,7 +256,8 @@ export class Transaction extends SpanClass implements TransactionInterface {
const maybeSampleRate = this.metadata.sampleRate;
const sample_rate = maybeSampleRate !== undefined ? maybeSampleRate.toString() : undefined;
- const { segment: user_segment } = hub.getScope().getUser() || {};
+ const scope = hub.getScope();
+ const { segment: user_segment } = (scope && scope.getUser()) || {};
const source = this.metadata.source;
@@ -276,8 +277,6 @@ export class Transaction extends SpanClass implements TransactionInterface {
// Uncomment if we want to make DSC immutable
// this._frozenDynamicSamplingContext = dsc;
- client.emit && client.emit('createDsc', dsc);
-
return dsc;
}
}
diff --git a/packages/core/src/tracing/utils.ts b/packages/core/src/tracing/utils.ts
index 624fff153270..5dc64f98d400 100644
--- a/packages/core/src/tracing/utils.ts
+++ b/packages/core/src/tracing/utils.ts
@@ -21,7 +21,7 @@ export { TRACEPARENT_REGEXP, extractTraceparentData } from '@sentry/utils';
export function getActiveTransaction(maybeHub?: Hub): T | undefined {
const hub = maybeHub || getCurrentHub();
const scope = hub.getScope();
- return scope.getTransaction() as T | undefined;
+ return scope && (scope.getTransaction() as T | undefined);
}
// so it can be used in manual instrumentation without necessitating a hard dependency on @sentry/utils
diff --git a/packages/core/test/lib/integrations/inboundfilters.test.ts b/packages/core/test/lib/integrations/inboundfilters.test.ts
index 7537f19d4d5d..ff9aca20270a 100644
--- a/packages/core/test/lib/integrations/inboundfilters.test.ts
+++ b/packages/core/test/lib/integrations/inboundfilters.test.ts
@@ -177,11 +177,6 @@ const MALFORMED_EVENT: Event = {
},
};
-const TRANSACTION_EVENT: Event = {
- message: 'transaction message',
- type: 'transaction',
-};
-
describe('InboundFilters', () => {
describe('_isSentryError', () => {
it('should work as expected', () => {
@@ -207,13 +202,6 @@ describe('InboundFilters', () => {
expect(eventProcessor(MESSAGE_EVENT, {})).toBe(null);
});
- it('ignores transaction event for filtering', () => {
- const eventProcessor = createInboundFiltersEventProcessor({
- ignoreErrors: ['transaction'],
- });
- expect(eventProcessor(TRANSACTION_EVENT, {})).toBe(TRANSACTION_EVENT);
- });
-
it('string filter with exact match', () => {
const eventProcessor = createInboundFiltersEventProcessor({
ignoreErrors: ['captureMessage'],
diff --git a/packages/core/test/lib/tracing/trace.test.ts b/packages/core/test/lib/tracing/trace.test.ts
deleted file mode 100644
index 064c41dc123a..000000000000
--- a/packages/core/test/lib/tracing/trace.test.ts
+++ /dev/null
@@ -1,189 +0,0 @@
-import { addTracingExtensions, Hub, makeMain } from '../../../src';
-import { trace } from '../../../src/tracing';
-import { getDefaultTestClientOptions, TestClient } from '../../mocks/client';
-
-beforeAll(() => {
- addTracingExtensions();
-});
-
-const enum Type {
- Sync = 'sync',
- Async = 'async',
-}
-
-let hub: Hub;
-let client: TestClient;
-
-describe('trace', () => {
- beforeEach(() => {
- const options = getDefaultTestClientOptions({ tracesSampleRate: 0.0 });
- client = new TestClient(options);
- hub = new Hub(client);
- makeMain(hub);
- });
-
- describe.each([
- // isSync, isError, callback, expectedReturnValue
- [Type.Async, false, () => Promise.resolve('async good'), 'async good'],
- [Type.Sync, false, () => 'sync good', 'sync good'],
- [Type.Async, true, () => Promise.reject('async bad'), 'async bad'],
- [
- Type.Sync,
- true,
- () => {
- throw 'sync bad';
- },
- 'sync bad',
- ],
- ])('with %s callback and error %s', (_type, isError, callback, expected) => {
- it('should return the same value as the callback', async () => {
- try {
- const result = await trace({ name: 'GET users/[id]' }, () => {
- return callback();
- });
- expect(result).toEqual(expected);
- } catch (e) {
- expect(e).toEqual(expected);
- }
- });
-
- it('should return the same value as the callback if transactions are undefined', async () => {
- // @ts-ignore we are force overriding the transaction return to be undefined
- // The `startTransaction` types are actually wrong - it can return undefined
- // if tracingExtensions are not enabled
- jest.spyOn(hub, 'startTransaction').mockReturnValue(undefined);
- try {
- const result = await trace({ name: 'GET users/[id]' }, () => {
- return callback();
- });
- expect(result).toEqual(expected);
- } catch (e) {
- expect(e).toEqual(expected);
- }
- });
-
- it('creates a transaction', async () => {
- let ref: any = undefined;
- client.on('finishTransaction', transaction => {
- ref = transaction;
- });
- try {
- await trace({ name: 'GET users/[id]' }, () => {
- return callback();
- });
- } catch (e) {
- //
- }
- expect(ref).toBeDefined();
-
- expect(ref.name).toEqual('GET users/[id]');
- expect(ref.status).toEqual(isError ? 'internal_error' : undefined);
- });
-
- it('allows traceparent information to be overriden', async () => {
- let ref: any = undefined;
- client.on('finishTransaction', transaction => {
- ref = transaction;
- });
- try {
- await trace(
- {
- name: 'GET users/[id]',
- parentSampled: true,
- traceId: '12345678901234567890123456789012',
- parentSpanId: '1234567890123456',
- },
- () => {
- return callback();
- },
- );
- } catch (e) {
- //
- }
- expect(ref).toBeDefined();
-
- expect(ref.sampled).toEqual(true);
- expect(ref.traceId).toEqual('12345678901234567890123456789012');
- expect(ref.parentSpanId).toEqual('1234567890123456');
- });
-
- it('allows for transaction to be mutated', async () => {
- let ref: any = undefined;
- client.on('finishTransaction', transaction => {
- ref = transaction;
- });
- try {
- await trace({ name: 'GET users/[id]' }, span => {
- if (span) {
- span.op = 'http.server';
- }
- return callback();
- });
- } catch (e) {
- //
- }
-
- expect(ref.op).toEqual('http.server');
- });
-
- it('creates a span with correct description', async () => {
- let ref: any = undefined;
- client.on('finishTransaction', transaction => {
- ref = transaction;
- });
- try {
- await trace({ name: 'GET users/[id]', parentSampled: true }, () => {
- return trace({ name: 'SELECT * from users' }, () => {
- return callback();
- });
- });
- } catch (e) {
- //
- }
-
- expect(ref.spanRecorder.spans).toHaveLength(2);
- expect(ref.spanRecorder.spans[1].description).toEqual('SELECT * from users');
- expect(ref.spanRecorder.spans[1].parentSpanId).toEqual(ref.spanId);
- expect(ref.spanRecorder.spans[1].status).toEqual(isError ? 'internal_error' : undefined);
- });
-
- it('allows for span to be mutated', async () => {
- let ref: any = undefined;
- client.on('finishTransaction', transaction => {
- ref = transaction;
- });
- try {
- await trace({ name: 'GET users/[id]', parentSampled: true }, () => {
- return trace({ name: 'SELECT * from users' }, childSpan => {
- if (childSpan) {
- childSpan.op = 'db.query';
- }
- return callback();
- });
- });
- } catch (e) {
- //
- }
-
- expect(ref.spanRecorder.spans).toHaveLength(2);
- expect(ref.spanRecorder.spans[1].op).toEqual('db.query');
- });
-
- it('calls `onError` hook', async () => {
- const onError = jest.fn();
- try {
- await trace(
- { name: 'GET users/[id]' },
- () => {
- return callback();
- },
- onError,
- );
- } catch (e) {
- expect(onError).toHaveBeenCalledTimes(1);
- expect(onError).toHaveBeenCalledWith(e);
- }
- expect(onError).toHaveBeenCalledTimes(isError ? 1 : 0);
- });
- });
-});
diff --git a/packages/e2e-tests/test-applications/standard-frontend-react/src/globals.d.ts b/packages/e2e-tests/test-applications/standard-frontend-react/src/globals.d.ts
index ffa61ca49acc..109dbcd55648 100644
--- a/packages/e2e-tests/test-applications/standard-frontend-react/src/globals.d.ts
+++ b/packages/e2e-tests/test-applications/standard-frontend-react/src/globals.d.ts
@@ -1,5 +1,4 @@
interface Window {
recordedTransactions?: string[];
capturedExceptionId?: string;
- sentryReplayId?: string;
}
diff --git a/packages/e2e-tests/test-applications/standard-frontend-react/src/index.tsx b/packages/e2e-tests/test-applications/standard-frontend-react/src/index.tsx
index ef820ec794b3..c6b8db266ac0 100644
--- a/packages/e2e-tests/test-applications/standard-frontend-react/src/index.tsx
+++ b/packages/e2e-tests/test-applications/standard-frontend-react/src/index.tsx
@@ -14,8 +14,6 @@ import {
import Index from './pages/Index';
import User from './pages/User';
-const replay = new Sentry.Replay();
-
Sentry.init({
dsn: process.env.REACT_APP_E2E_TEST_DSN,
integrations: [
@@ -28,22 +26,11 @@ Sentry.init({
matchRoutes,
),
}),
- replay,
],
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
tracesSampleRate: 1.0,
release: 'e2e-test',
-
- // Always capture replays, so we can test this properly
- replaysSessionSampleRate: 1.0,
- replaysOnErrorSampleRate: 0.0,
-});
-
-Object.defineProperty(window, 'sentryReplayId', {
- get() {
- return replay['_replay'].session.id;
- },
});
Sentry.addGlobalEventProcessor(event => {
diff --git a/packages/e2e-tests/test-applications/standard-frontend-react/tests/behaviour-test.spec.ts b/packages/e2e-tests/test-applications/standard-frontend-react/tests/behaviour-test.spec.ts
index fb2d291dd70d..795d610a4b08 100644
--- a/packages/e2e-tests/test-applications/standard-frontend-react/tests/behaviour-test.spec.ts
+++ b/packages/e2e-tests/test-applications/standard-frontend-react/tests/behaviour-test.spec.ts
@@ -1,6 +1,5 @@
import { test, expect } from '@playwright/test';
import axios, { AxiosError } from 'axios';
-import { ReplayRecordingData } from './fixtures/ReplayRecordingData';
const EVENT_POLLING_TIMEOUT = 30_000;
@@ -170,85 +169,3 @@ test('Sends a navigation transaction to Sentry', async ({ page }) => {
expect(hadPageNavigationTransaction).toBe(true);
});
-
-test('Sends a Replay recording to Sentry', async ({ browser }) => {
- const context = await browser.newContext();
- const page = await context.newPage();
-
- await page.goto('/');
-
- const replayId = await page.waitForFunction(() => {
- return window.sentryReplayId;
- });
-
- // Wait for replay to be sent
-
- if (replayId === undefined) {
- throw new Error("Application didn't set a replayId");
- }
-
- console.log(`Polling for replay with ID: ${replayId}`);
-
- await expect
- .poll(
- async () => {
- try {
- const response = await axios.get(
- `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/replays/${replayId}/`,
- { headers: { Authorization: `Bearer ${authToken}` } },
- );
-
- return response.status;
- } catch (e) {
- if (e instanceof AxiosError && e.response) {
- if (e.response.status !== 404) {
- throw e;
- } else {
- return e.response.status;
- }
- } else {
- throw e;
- }
- }
- },
- {
- timeout: EVENT_POLLING_TIMEOUT,
- },
- )
- .toBe(200);
-
- // now fetch the first recording segment
- await expect
- .poll(
- async () => {
- try {
- const response = await axios.get(
- `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/replays/${replayId}/recording-segments/?cursor=100%3A0%3A1`,
- { headers: { Authorization: `Bearer ${authToken}` } },
- );
-
- return {
- status: response.status,
- data: response.data,
- };
- } catch (e) {
- if (e instanceof AxiosError && e.response) {
- if (e.response.status !== 404) {
- throw e;
- } else {
- return e.response.status;
- }
- } else {
- throw e;
- }
- }
- },
- {
- timeout: EVENT_POLLING_TIMEOUT,
- },
- )
- .toEqual({
- status: 200,
- data: ReplayRecordingData,
- });
-});
diff --git a/packages/e2e-tests/test-applications/standard-frontend-react/tests/fixtures/ReplayRecordingData.ts b/packages/e2e-tests/test-applications/standard-frontend-react/tests/fixtures/ReplayRecordingData.ts
deleted file mode 100644
index 318fc368f7b9..000000000000
--- a/packages/e2e-tests/test-applications/standard-frontend-react/tests/fixtures/ReplayRecordingData.ts
+++ /dev/null
@@ -1,225 +0,0 @@
-import { expect } from '@playwright/test';
-
-export const ReplayRecordingData = [
- [
- { type: 4, data: { href: 'http://localhost:3000/', width: 1280, height: 720 }, timestamp: expect.any(Number) },
- {
- type: 2,
- data: {
- node: {
- type: 0,
- childNodes: [
- { type: 1, name: 'html', publicId: '', systemId: '', id: 2 },
- {
- type: 2,
- tagName: 'html',
- attributes: { lang: 'en' },
- childNodes: [
- {
- type: 2,
- tagName: 'head',
- attributes: {},
- childNodes: [
- { type: 2, tagName: 'meta', attributes: { charset: 'utf-8' }, childNodes: [], id: 5 },
- {
- type: 2,
- tagName: 'meta',
- attributes: { name: 'viewport', content: 'width=device-width,initial-scale=1' },
- childNodes: [],
- id: 6,
- },
- {
- type: 2,
- tagName: 'meta',
- attributes: { name: 'theme-color', content: '#000000' },
- childNodes: [],
- id: 7,
- },
- {
- type: 2,
- tagName: 'title',
- attributes: {},
- childNodes: [{ type: 3, textContent: '***** ***', id: 9 }],
- id: 8,
- },
- ],
- id: 4,
- },
- {
- type: 2,
- tagName: 'body',
- attributes: {},
- childNodes: [
- {
- type: 2,
- tagName: 'noscript',
- attributes: {},
- childNodes: [{ type: 3, textContent: '*** **** ** ****** ********** ** *** **** ****', id: 12 }],
- id: 11,
- },
- { type: 2, tagName: 'div', attributes: { id: 'root' }, childNodes: [], id: 13 },
- ],
- id: 10,
- },
- ],
- id: 3,
- },
- ],
- id: 1,
- },
- initialOffset: { left: 0, top: 0 },
- },
- timestamp: expect.any(Number),
- },
- {
- type: 5,
- timestamp: expect.any(Number),
- data: {
- tag: 'performanceSpan',
- payload: {
- op: 'memory',
- description: 'memory',
- startTimestamp: expect.any(Number),
- endTimestamp: expect.any(Number),
- data: {
- memory: {
- jsHeapSizeLimit: expect.any(Number),
- totalJSHeapSize: expect.any(Number),
- usedJSHeapSize: expect.any(Number),
- },
- },
- },
- },
- },
- {
- type: 3,
- data: {
- source: 0,
- texts: [],
- attributes: [],
- removes: [],
- adds: [
- {
- parentId: 13,
- nextId: null,
- node: {
- type: 2,
- tagName: 'a',
- attributes: { id: 'navigation', href: 'http://localhost:3000/user/5' },
- childNodes: [],
- id: 14,
- },
- },
- { parentId: 14, nextId: null, node: { type: 3, textContent: '********', id: 15 } },
- {
- parentId: 13,
- nextId: 14,
- node: {
- type: 2,
- tagName: 'input',
- attributes: { type: 'button', id: 'exception-button', value: '******* *********' },
- childNodes: [],
- id: 16,
- },
- },
- ],
- },
- timestamp: expect.any(Number),
- },
- {
- type: 3,
- data: { source: 5, text: 'Capture Exception', isChecked: false, id: 16 },
- timestamp: expect.any(Number),
- },
- ],
- [
- {
- type: 5,
- timestamp: expect.any(Number),
- data: {
- tag: 'performanceSpan',
- payload: {
- op: 'navigation.navigate',
- description: 'http://localhost:3000/',
- startTimestamp: expect.any(Number),
- endTimestamp: expect.any(Number),
- data: { size: expect.any(Number), duration: expect.any(Number) },
- },
- },
- },
- {
- type: 5,
- timestamp: expect.any(Number),
- data: {
- tag: 'performanceSpan',
- payload: {
- op: 'resource.script',
- description: expect.stringMatching(/http:\/\/localhost:3000\/static\/js\/main.(\w+).js/),
- startTimestamp: expect.any(Number),
- endTimestamp: expect.any(Number),
- data: { size: expect.any(Number), encodedBodySize: expect.any(Number) },
- },
- },
- },
- {
- type: 5,
- timestamp: expect.any(Number),
- data: {
- tag: 'performanceSpan',
- payload: {
- op: 'largest-contentful-paint',
- description: 'largest-contentful-paint',
- startTimestamp: expect.any(Number),
- endTimestamp: expect.any(Number),
- data: { value: expect.any(Number), size: expect.any(Number), nodeId: 16 },
- },
- },
- },
- {
- type: 5,
- timestamp: expect.any(Number),
- data: {
- tag: 'performanceSpan',
- payload: {
- op: 'paint',
- description: 'first-paint',
- startTimestamp: expect.any(Number),
- endTimestamp: expect.any(Number),
- },
- },
- },
- {
- type: 5,
- timestamp: expect.any(Number),
- data: {
- tag: 'performanceSpan',
- payload: {
- op: 'paint',
- description: 'first-contentful-paint',
- startTimestamp: expect.any(Number),
- endTimestamp: expect.any(Number),
- },
- },
- },
- {
- type: 5,
- timestamp: expect.any(Number),
- data: {
- tag: 'performanceSpan',
- payload: {
- op: 'memory',
- description: 'memory',
- startTimestamp: expect.any(Number),
- endTimestamp: expect.any(Number),
- data: {
- memory: {
- jsHeapSizeLimit: expect.any(Number),
- totalJSHeapSize: expect.any(Number),
- usedJSHeapSize: expect.any(Number),
- },
- },
- },
- },
- },
- ],
-];
diff --git a/packages/ember/addon/instance-initializers/sentry-performance.ts b/packages/ember/addon/instance-initializers/sentry-performance.ts
index 0001e6c5f97c..ac3c08e46079 100644
--- a/packages/ember/addon/instance-initializers/sentry-performance.ts
+++ b/packages/ember/addon/instance-initializers/sentry-performance.ts
@@ -386,7 +386,7 @@ export async function instrumentForPerformance(appInstance: ApplicationInstance)
// Maintaining backwards compatibility with config.browserTracingOptions, but passing it with Sentry options is preferred.
const browserTracingOptions = config.browserTracingOptions || config.sentry.browserTracingOptions || {};
- const { BrowserTracing } = await import('@sentry/browser');
+ const tracing = await import('@sentry/tracing');
const idleTimeout = config.transitionTimeout || 5000;
@@ -394,7 +394,7 @@ export async function instrumentForPerformance(appInstance: ApplicationInstance)
sentryConfig['integrations'] = [
...existingIntegrations,
- new BrowserTracing({
+ new tracing.Integrations.BrowserTracing({
routingInstrumentation: (customStartTransaction, startTransactionOnPageLoad) => {
const routerMain = appInstance.lookup('router:main');
let routerService = appInstance.lookup('service:router') as
@@ -421,7 +421,7 @@ export async function instrumentForPerformance(appInstance: ApplicationInstance)
];
class FakeBrowserTracingClass {
- static id = 'BrowserTracing';
+ static id = tracing.BROWSER_TRACING_INTEGRATION_ID;
public name = FakeBrowserTracingClass.id;
setupOnce() {
// noop - We're just faking this class for a lookup
diff --git a/packages/ember/package.json b/packages/ember/package.json
index 5ba2b5dcfead..2ac858180668 100644
--- a/packages/ember/package.json
+++ b/packages/ember/package.json
@@ -30,6 +30,7 @@
"dependencies": {
"@embroider/macros": "^1.9.0",
"@sentry/browser": "7.44.2",
+ "@sentry/tracing": "7.44.2",
"@sentry/types": "7.44.2",
"@sentry/utils": "7.44.2",
"ember-auto-import": "^1.12.1 || ^2.4.3",
diff --git a/packages/eslint-config-sdk/src/index.js b/packages/eslint-config-sdk/src/index.js
index 05ec68cff509..d09be9e2e67d 100644
--- a/packages/eslint-config-sdk/src/index.js
+++ b/packages/eslint-config-sdk/src/index.js
@@ -161,9 +161,6 @@ module.exports = {
// All imports should be accounted for
'import/no-extraneous-dependencies': 'error',
-
- // Do not allow usage of functions we do not polyfill for ES5
- '@sentry-internal/sdk/no-unsupported-es6-methods': 'error',
},
},
{
diff --git a/packages/eslint-plugin-sdk/src/index.js b/packages/eslint-plugin-sdk/src/index.js
index 31d8e932d904..31ac785abf5e 100644
--- a/packages/eslint-plugin-sdk/src/index.js
+++ b/packages/eslint-plugin-sdk/src/index.js
@@ -13,6 +13,5 @@ module.exports = {
'no-optional-chaining': require('./rules/no-optional-chaining'),
'no-nullish-coalescing': require('./rules/no-nullish-coalescing'),
'no-eq-empty': require('./rules/no-eq-empty'),
- 'no-unsupported-es6-methods': require('./rules/no-unsupported-es6-methods'),
},
};
diff --git a/packages/eslint-plugin-sdk/src/rules/no-unsupported-es6-methods.js b/packages/eslint-plugin-sdk/src/rules/no-unsupported-es6-methods.js
deleted file mode 100644
index 85d32fb20e66..000000000000
--- a/packages/eslint-plugin-sdk/src/rules/no-unsupported-es6-methods.js
+++ /dev/null
@@ -1,35 +0,0 @@
-'use strict';
-
-/**
- * Taken and adapted from https://github.com/nkt/eslint-plugin-es5/blob/master/src/rules/no-es6-methods.js
- */
-
-module.exports = {
- meta: {
- docs: {
- description: 'Forbid methods added in ES6 which are not polyfilled by Sentry.',
- },
- schema: [],
- },
- create(context) {
- return {
- CallExpression(node) {
- if (!node.callee || !node.callee.property) {
- return;
- }
- const functionName = node.callee.property.name;
-
- const es6ArrayFunctions = ['copyWithin', 'values', 'fill'];
- const es6StringFunctions = ['repeat'];
-
- const es6Functions = [].concat(es6ArrayFunctions, es6StringFunctions);
- if (es6Functions.indexOf(functionName) > -1) {
- context.report({
- node: node.callee.property,
- message: `ES6 methods not allowed: ${functionName}`,
- });
- }
- },
- };
- },
-};
diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json
index 732c0836a08f..1016f7cfa754 100644
--- a/packages/gatsby/package.json
+++ b/packages/gatsby/package.json
@@ -22,6 +22,7 @@
"dependencies": {
"@sentry/core": "7.44.2",
"@sentry/react": "7.44.2",
+ "@sentry/tracing": "7.44.2",
"@sentry/types": "7.44.2",
"@sentry/utils": "7.44.2",
"@sentry/webpack-plugin": "1.19.0"
diff --git a/packages/gatsby/src/index.ts b/packages/gatsby/src/index.ts
index 7c603d040693..6957f7337700 100644
--- a/packages/gatsby/src/index.ts
+++ b/packages/gatsby/src/index.ts
@@ -1,3 +1,4 @@
export * from '@sentry/react';
+export { Integrations } from '@sentry/tracing';
export { init } from './sdk';
diff --git a/packages/gatsby/src/utils/integrations.ts b/packages/gatsby/src/utils/integrations.ts
index 94ef28f21272..680ef61765cc 100644
--- a/packages/gatsby/src/utils/integrations.ts
+++ b/packages/gatsby/src/utils/integrations.ts
@@ -1,5 +1,5 @@
import { hasTracingEnabled } from '@sentry/core';
-import { BrowserTracing } from '@sentry/react';
+import * as Tracing from '@sentry/tracing';
import type { Integration } from '@sentry/types';
import type { GatsbyOptions } from './types';
@@ -31,8 +31,11 @@ export function getIntegrationsFromOptions(options: GatsbyOptions): UserIntegrat
* @param isTracingEnabled Whether the user has enabled tracing.
*/
function getIntegrationsFromArray(userIntegrations: Integration[], isTracingEnabled: boolean): Integration[] {
- if (isTracingEnabled && !userIntegrations.some(integration => integration.name === BrowserTracing.name)) {
- userIntegrations.push(new BrowserTracing());
+ if (
+ isTracingEnabled &&
+ !userIntegrations.some(integration => integration.name === Tracing.Integrations.BrowserTracing.name)
+ ) {
+ userIntegrations.push(new Tracing.Integrations.BrowserTracing());
}
return userIntegrations;
}
diff --git a/packages/gatsby/test/gatsby-browser.test.ts b/packages/gatsby/test/gatsby-browser.test.ts
index b67305042c71..00be51488d5f 100644
--- a/packages/gatsby/test/gatsby-browser.test.ts
+++ b/packages/gatsby/test/gatsby-browser.test.ts
@@ -2,7 +2,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { onClientEntry } from '../gatsby-browser';
-import { BrowserTracing } from '../src/index';
(global as any).__SENTRY_RELEASE__ = '683f3a6ab819d47d23abfca9a914c81f0524d35b';
(global as any).__SENTRY_DSN__ = 'https://examplePublicKey@o0.ingest.sentry.io/0';
@@ -21,11 +20,11 @@ global.console.warn = jest.fn();
global.console.error = jest.fn();
let tracingAddExtensionMethods = jest.fn();
-jest.mock('@sentry/core', () => {
- const original = jest.requireActual('@sentry/core');
+jest.mock('@sentry/tracing', () => {
+ const original = jest.requireActual('@sentry/tracing');
return {
...original,
- addTracingExtensions: (...args: any[]) => {
+ addExtensionMethods: (...args: any[]) => {
tracingAddExtensionMethods(...args);
},
};
@@ -141,7 +140,8 @@ describe('onClientEntry', () => {
});
it('only defines a single `BrowserTracing` integration', () => {
- const integrations = [new BrowserTracing()];
+ const Tracing = jest.requireActual('@sentry/tracing');
+ const integrations = [new Tracing.Integrations.BrowserTracing()];
onClientEntry(undefined, { tracesSampleRate: 0.5, integrations });
expect(sentryInit).toHaveBeenLastCalledWith(
diff --git a/packages/gatsby/test/sdk.test.ts b/packages/gatsby/test/sdk.test.ts
index 082fd771060b..1c4342a13a4b 100644
--- a/packages/gatsby/test/sdk.test.ts
+++ b/packages/gatsby/test/sdk.test.ts
@@ -1,4 +1,5 @@
-import { BrowserTracing, init, SDK_VERSION } from '@sentry/react';
+import { init, SDK_VERSION } from '@sentry/react';
+import { Integrations } from '@sentry/tracing';
import type { Integration } from '@sentry/types';
import { init as gatsbyInit } from '../src/sdk';
@@ -57,8 +58,6 @@ describe('Initialize React SDK', () => {
});
});
-type TestArgs = [string, Integration[], GatsbyOptions, string[]];
-
describe('Integrations from options', () => {
afterEach(() => reactInit.mockClear());
@@ -66,39 +65,72 @@ describe('Integrations from options', () => {
['tracing disabled, no integrations', [], {}, []],
['tracing enabled, no integrations', [], { tracesSampleRate: 1 }, ['BrowserTracing']],
[
- 'tracing disabled, with BrowserTracing as an array',
+ 'tracing disabled, with Integrations.BrowserTracing as an array',
[],
- { integrations: [new BrowserTracing()] },
+ { integrations: [new Integrations.BrowserTracing()] },
['BrowserTracing'],
],
[
- 'tracing disabled, with BrowserTracing as a function',
+ 'tracing disabled, with Integrations.BrowserTracing as a function',
[],
{
- integrations: () => [new BrowserTracing()],
+ integrations: () => [new Integrations.BrowserTracing()],
},
['BrowserTracing'],
],
[
- 'tracing enabled, with BrowserTracing as an array',
+ 'tracing enabled, with Integrations.BrowserTracing as an array',
[],
- { tracesSampleRate: 1, integrations: [new BrowserTracing()] },
+ { tracesSampleRate: 1, integrations: [new Integrations.BrowserTracing()] },
['BrowserTracing'],
],
[
- 'tracing enabled, with BrowserTracing as a function',
+ 'tracing enabled, with Integrations.BrowserTracing as a function',
[],
- { tracesSampleRate: 1, integrations: () => [new BrowserTracing()] },
+ { tracesSampleRate: 1, integrations: () => [new Integrations.BrowserTracing()] },
['BrowserTracing'],
],
- ] as TestArgs[])(
- '%s',
- (_testName, defaultIntegrations: Integration[], options: GatsbyOptions, expectedIntNames: string[]) => {
- gatsbyInit(options);
- const integrations: UserIntegrations = reactInit.mock.calls[0][0].integrations;
- const arrIntegrations = Array.isArray(integrations) ? integrations : integrations(defaultIntegrations);
- expect(arrIntegrations).toHaveLength(expectedIntNames.length);
- arrIntegrations.map((integration, idx) => expect(integration.name).toStrictEqual(expectedIntNames[idx]));
- },
- );
+ [
+ 'tracing enabled, with another integration as an array',
+ [],
+ { tracesSampleRate: 1, integrations: [new Integrations.Express()] },
+ ['Express', 'BrowserTracing'],
+ ],
+ [
+ 'tracing enabled, with another integration as a function',
+ [],
+ { tracesSampleRate: 1, integrations: () => [new Integrations.Express()] },
+ ['Express', 'BrowserTracing'],
+ ],
+ [
+ 'tracing disabled, with another integration as an array',
+ [],
+ { integrations: [new Integrations.Express()] },
+ ['Express'],
+ ],
+ [
+ 'tracing disabled, with another integration as a function',
+ [],
+ { integrations: () => [new Integrations.Express()] },
+ ['Express'],
+ ],
+ [
+ 'merges integrations with user integrations as a function',
+ [new Integrations.Mongo()],
+ {
+ tracesSampleRate: 1,
+ integrations: (defaultIntegrations: Integration[]): Integration[] => [
+ ...defaultIntegrations,
+ new Integrations.Express(),
+ ],
+ },
+ ['Mongo', 'Express', 'BrowserTracing'],
+ ],
+ ])('%s', (_testName, defaultIntegrations: Integration[], options: GatsbyOptions, expectedIntNames: string[]) => {
+ gatsbyInit(options);
+ const integrations: UserIntegrations = reactInit.mock.calls[0][0].integrations;
+ const arrIntegrations = Array.isArray(integrations) ? integrations : integrations(defaultIntegrations);
+ expect(arrIntegrations).toHaveLength(expectedIntNames.length);
+ arrIntegrations.map((integration, idx) => expect(integration.name).toStrictEqual(expectedIntNames[idx]));
+ });
});
diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json
index 8b32553b1f9d..f341b0609519 100644
--- a/packages/nextjs/package.json
+++ b/packages/nextjs/package.json
@@ -22,6 +22,7 @@
"@sentry/integrations": "7.44.2",
"@sentry/node": "7.44.2",
"@sentry/react": "7.44.2",
+ "@sentry/tracing": "7.44.2",
"@sentry/types": "7.44.2",
"@sentry/utils": "7.44.2",
"@sentry/webpack-plugin": "1.20.0",
diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts
index ece6bf78db6a..79dcd5219cd4 100644
--- a/packages/nextjs/src/client/index.ts
+++ b/packages/nextjs/src/client/index.ts
@@ -1,13 +1,8 @@
import { hasTracingEnabled } from '@sentry/core';
import { RewriteFrames } from '@sentry/integrations';
import type { BrowserOptions } from '@sentry/react';
-import {
- BrowserTracing,
- configureScope,
- defaultRequestInstrumentationOptions,
- init as reactInit,
- Integrations,
-} from '@sentry/react';
+import { configureScope, init as reactInit, Integrations } from '@sentry/react';
+import { BrowserTracing, defaultRequestInstrumentationOptions } from '@sentry/tracing';
import type { EventProcessor } from '@sentry/types';
import { addOrUpdateIntegration } from '@sentry/utils';
diff --git a/packages/nextjs/src/edge/edgeclient.ts b/packages/nextjs/src/edge/edgeclient.ts
index 16aed66d1ca4..a5b38d651aed 100644
--- a/packages/nextjs/src/edge/edgeclient.ts
+++ b/packages/nextjs/src/edge/edgeclient.ts
@@ -1,5 +1,5 @@
import type { Scope } from '@sentry/core';
-import { addTracingExtensions, BaseClient, SDK_VERSION } from '@sentry/core';
+import { BaseClient, SDK_VERSION } from '@sentry/core';
import type { ClientOptions, Event, EventHint, Severity, SeverityLevel } from '@sentry/types';
import { eventFromMessage, eventFromUnknownInput } from './eventbuilder';
@@ -28,9 +28,6 @@ export class EdgeClient extends BaseClient {
version: SDK_VERSION,
};
- // The Edge client always supports tracing
- addTracingExtensions();
-
super(options);
}
diff --git a/packages/nextjs/src/edge/index.ts b/packages/nextjs/src/edge/index.ts
index f7785aaa06d6..6f8cd2f42cc4 100644
--- a/packages/nextjs/src/edge/index.ts
+++ b/packages/nextjs/src/edge/index.ts
@@ -1,3 +1,5 @@
+import '@sentry/tracing'; // Allow people to call tracing API methods without explicitly importing the tracing package.
+
import { getCurrentHub, getIntegrationsToSetup, initAndBind, Integrations as CoreIntegrations } from '@sentry/core';
import type { Options } from '@sentry/types';
import {
diff --git a/packages/nextjs/src/server/utils/wrapperUtils.ts b/packages/nextjs/src/server/utils/wrapperUtils.ts
index 9fa91fbbee5a..ae05b3f16b8d 100644
--- a/packages/nextjs/src/server/utils/wrapperUtils.ts
+++ b/packages/nextjs/src/server/utils/wrapperUtils.ts
@@ -1,4 +1,5 @@
-import { captureException, getActiveTransaction, getCurrentHub, startTransaction } from '@sentry/core';
+import { captureException, getCurrentHub, startTransaction } from '@sentry/core';
+import { getActiveTransaction } from '@sentry/tracing';
import type { Transaction } from '@sentry/types';
import { baggageHeaderToDynamicSamplingContext, extractTraceparentData } from '@sentry/utils';
import * as domain from 'domain';
diff --git a/packages/nextjs/src/server/wrapApiHandlerWithSentry.ts b/packages/nextjs/src/server/wrapApiHandlerWithSentry.ts
index 37ef93bf30cc..5bed5dca3135 100644
--- a/packages/nextjs/src/server/wrapApiHandlerWithSentry.ts
+++ b/packages/nextjs/src/server/wrapApiHandlerWithSentry.ts
@@ -1,10 +1,10 @@
import { hasTracingEnabled } from '@sentry/core';
import { captureException, getCurrentHub, startTransaction } from '@sentry/node';
+import { extractTraceparentData } from '@sentry/tracing';
import type { Transaction } from '@sentry/types';
import {
addExceptionMechanism,
baggageHeaderToDynamicSamplingContext,
- extractTraceparentData,
isString,
logger,
objectify,
diff --git a/packages/nextjs/src/server/wrapServerComponentWithSentry.ts b/packages/nextjs/src/server/wrapServerComponentWithSentry.ts
index 12ff9ceb5f72..eab32207236c 100644
--- a/packages/nextjs/src/server/wrapServerComponentWithSentry.ts
+++ b/packages/nextjs/src/server/wrapServerComponentWithSentry.ts
@@ -1,4 +1,4 @@
-import { addTracingExtensions, captureException, getCurrentHub, startTransaction } from '@sentry/core';
+import { captureException, getCurrentHub, startTransaction } from '@sentry/core';
import { baggageHeaderToDynamicSamplingContext, extractTraceparentData } from '@sentry/utils';
import * as domain from 'domain';
@@ -11,8 +11,6 @@ export function wrapServerComponentWithSentry any>
appDirComponent: F,
context: ServerComponentContext,
): F {
- addTracingExtensions();
-
const { componentRoute, componentType } = context;
// Even though users may define server components as async functions, for the client bundles
diff --git a/packages/nextjs/test/clientSdk.test.ts b/packages/nextjs/test/clientSdk.test.ts
index ed3bb666d58d..5bfb29434523 100644
--- a/packages/nextjs/test/clientSdk.test.ts
+++ b/packages/nextjs/test/clientSdk.test.ts
@@ -1,6 +1,7 @@
import { BaseClient, getCurrentHub } from '@sentry/core';
import * as SentryReact from '@sentry/react';
-import { BrowserTracing, WINDOW } from '@sentry/react';
+import { WINDOW } from '@sentry/react';
+import { Integrations as TracingIntegrations } from '@sentry/tracing';
import type { Integration } from '@sentry/types';
import type { UserIntegrationsFunction } from '@sentry/utils';
import { logger } from '@sentry/utils';
@@ -8,6 +9,8 @@ import { JSDOM } from 'jsdom';
import { init, Integrations, nextRouterInstrumentation } from '../src/client';
+const { BrowserTracing } = TracingIntegrations;
+
const reactInit = jest.spyOn(SentryReact, 'init');
const captureEvent = jest.spyOn(BaseClient.prototype, 'captureEvent');
const loggerLogSpy = jest.spyOn(logger, 'log');
diff --git a/packages/nextjs/test/config/withSentry.test.ts b/packages/nextjs/test/config/withSentry.test.ts
index 92315374836b..dfc06c9bcf7e 100644
--- a/packages/nextjs/test/config/withSentry.test.ts
+++ b/packages/nextjs/test/config/withSentry.test.ts
@@ -1,5 +1,4 @@
import * as hub from '@sentry/core';
-import { addTracingExtensions } from '@sentry/core';
import * as Sentry from '@sentry/node';
import type { Client, ClientOptions } from '@sentry/types';
import type { NextApiRequest, NextApiResponse } from 'next';
@@ -7,10 +6,6 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { withSentry } from '../../src/server';
import type { AugmentedNextApiResponse, NextApiHandler } from '../../src/server/types';
-// The wrap* functions require the hub to have tracing extensions. This is normally called by the NodeClient
-// constructor but the client isn't used in these tests.
-addTracingExtensions();
-
const FLUSH_DURATION = 200;
async function sleep(ms: number): Promise {
diff --git a/packages/nextjs/test/config/wrappers.test.ts b/packages/nextjs/test/config/wrappers.test.ts
index 444d45513ccb..68c598e9707f 100644
--- a/packages/nextjs/test/config/wrappers.test.ts
+++ b/packages/nextjs/test/config/wrappers.test.ts
@@ -1,5 +1,4 @@
import * as SentryCore from '@sentry/core';
-import { addTracingExtensions } from '@sentry/core';
import * as SentryNode from '@sentry/node';
import type { IncomingMessage, ServerResponse } from 'http';
@@ -7,10 +6,6 @@ import { wrapGetInitialPropsWithSentry, wrapGetServerSidePropsWithSentry } from
const startTransactionSpy = jest.spyOn(SentryCore, 'startTransaction');
-// The wrap* functions require the hub to have tracing extensions. This is normally called by the NodeClient
-// constructor but the client isn't used in these tests.
-addTracingExtensions();
-
describe('data-fetching function wrappers', () => {
const route = '/tricks/[trickName]';
let req: IncomingMessage;
diff --git a/packages/nextjs/test/edge/edgeWrapperUtils.test.ts b/packages/nextjs/test/edge/edgeWrapperUtils.test.ts
index cdc7cc4986e2..852ceb5628b4 100644
--- a/packages/nextjs/test/edge/edgeWrapperUtils.test.ts
+++ b/packages/nextjs/test/edge/edgeWrapperUtils.test.ts
@@ -1,12 +1,7 @@
import * as coreSdk from '@sentry/core';
-import { addTracingExtensions } from '@sentry/core';
import { withEdgeWrapping } from '../../src/edge/utils/edgeWrapperUtils';
-// The wrap* functions require the hub to have tracing extensions. This is normally called by the EdgeClient
-// constructor but the client isn't used in these tests.
-addTracingExtensions();
-
// @ts-ignore Request does not exist on type Global
const origRequest = global.Request;
// @ts-ignore Response does not exist on type Global
diff --git a/packages/nextjs/test/edge/withSentryAPI.test.ts b/packages/nextjs/test/edge/withSentryAPI.test.ts
index 08a91e0c5e11..2ecbdf22a96e 100644
--- a/packages/nextjs/test/edge/withSentryAPI.test.ts
+++ b/packages/nextjs/test/edge/withSentryAPI.test.ts
@@ -2,10 +2,6 @@ import * as coreSdk from '@sentry/core';
import { wrapApiHandlerWithSentry } from '../../src/edge';
-// The wrap* functions require the hub to have tracing extensions. This is normally called by the EdgeClient
-// constructor but the client isn't used in these tests.
-coreSdk.addTracingExtensions();
-
// @ts-ignore Request does not exist on type Global
const origRequest = global.Request;
// @ts-ignore Response does not exist on type Global
diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json
index e63a6e940dca..f1661102a963 100644
--- a/packages/node-integration-tests/package.json
+++ b/packages/node-integration-tests/package.json
@@ -9,7 +9,6 @@
"scripts": {
"clean": "rimraf -g **/node_modules",
"prisma:init": "(cd suites/tracing/prisma-orm && ts-node ./setup.ts)",
- "prisma:init:new": "(cd suites/tracing-new/prisma-orm && ts-node ./setup.ts)",
"lint": "run-s lint:prettier lint:eslint",
"lint:eslint": "eslint . --format stylish",
"lint:prettier": "prettier --check \"{suites,utils}/**/*.ts\"",
@@ -17,7 +16,7 @@
"fix:eslint": "eslint . --format stylish --fix",
"fix:prettier": "prettier --write \"{suites,utils}/**/*.ts\"",
"type-check": "tsc",
- "pretest": "run-s --silent prisma:init prisma:init:new",
+ "pretest": "run-s --silent prisma:init",
"test": "ts-node ./utils/run-tests.ts",
"test:watch": "yarn test --watch"
},
diff --git a/packages/node-integration-tests/suites/express/tracing/server.ts b/packages/node-integration-tests/suites/express/tracing/server.ts
index e857621ad22e..faf5a50f95ed 100644
--- a/packages/node-integration-tests/suites/express/tracing/server.ts
+++ b/packages/node-integration-tests/suites/express/tracing/server.ts
@@ -1,4 +1,5 @@
import * as Sentry from '@sentry/node';
+import * as Tracing from '@sentry/tracing';
import cors from 'cors';
import express from 'express';
@@ -7,7 +8,7 @@ const app = express();
Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '1.0',
- integrations: [new Sentry.Integrations.Http({ tracing: true }), new Sentry.Integrations.Express({ app })],
+ integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })],
tracesSampleRate: 1.0,
});
diff --git a/packages/node-integration-tests/suites/tracing-new/apollo-graphql/scenario.ts b/packages/node-integration-tests/suites/tracing-new/apollo-graphql/scenario.ts
deleted file mode 100644
index 5bd8aa815cbe..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/apollo-graphql/scenario.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import * as Sentry from '@sentry/node';
-import { ApolloServer, gql } from 'apollo-server';
-
-Sentry.init({
- dsn: 'https://public@dsn.ingest.sentry.io/1337',
- release: '1.0',
- tracesSampleRate: 1.0,
- integrations: [new Sentry.Integrations.GraphQL(), new Sentry.Integrations.Apollo()],
-});
-
-const typeDefs = gql`
- type Query {
- hello: String
- }
-`;
-
-const resolvers = {
- Query: {
- hello: () => {
- return 'Hello world!';
- },
- },
-};
-
-const server = new ApolloServer({
- typeDefs,
- resolvers,
-});
-
-const transaction = Sentry.startTransaction({ name: 'test_transaction', op: 'transaction' });
-
-Sentry.configureScope(scope => {
- scope.setSpan(transaction);
-});
-
-void (async () => {
- // Ref: https://www.apollographql.com/docs/apollo-server/testing/testing/#testing-using-executeoperation
- await server.executeOperation({
- query: '{hello}',
- });
-
- transaction.finish();
-})();
diff --git a/packages/node-integration-tests/suites/tracing-new/apollo-graphql/test.ts b/packages/node-integration-tests/suites/tracing-new/apollo-graphql/test.ts
deleted file mode 100644
index 128f8a2f164b..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/apollo-graphql/test.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { assertSentryTransaction, conditionalTest, TestEnv } from '../../../utils';
-
-// Node 10 is not supported by `graphql-js`
-// Ref: https://github.com/graphql/graphql-js/blob/main/package.json
-conditionalTest({ min: 12 })('GraphQL/Apollo Tests', () => {
- test('should instrument GraphQL and Apollo Server.', async () => {
- const env = await TestEnv.init(__dirname);
- const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });
-
- expect(envelope).toHaveLength(3);
-
- const transaction = envelope[2];
- const parentSpanId = (transaction as any)?.contexts?.trace?.span_id;
- const graphqlSpanId = (transaction as any)?.spans?.[0].span_id;
-
- expect(parentSpanId).toBeDefined();
- expect(graphqlSpanId).toBeDefined();
-
- assertSentryTransaction(transaction, {
- transaction: 'test_transaction',
- spans: [
- {
- description: 'execute',
- op: 'graphql.execute',
- parent_span_id: parentSpanId,
- },
- {
- description: 'Query.hello',
- op: 'graphql.resolve',
- parent_span_id: graphqlSpanId,
- },
- ],
- });
- });
-});
diff --git a/packages/node-integration-tests/suites/tracing-new/auto-instrument/mongodb/scenario.ts b/packages/node-integration-tests/suites/tracing-new/auto-instrument/mongodb/scenario.ts
deleted file mode 100644
index 31d7356765e9..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/auto-instrument/mongodb/scenario.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import * as Sentry from '@sentry/node';
-import { MongoClient } from 'mongodb';
-
-// suppress logging of the mongo download
-global.console.log = () => null;
-
-Sentry.init({
- dsn: 'https://public@dsn.ingest.sentry.io/1337',
- release: '1.0',
- tracesSampleRate: 1.0,
- integrations: [...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations()],
-});
-
-const client = new MongoClient(process.env.MONGO_URL || '', {
- useUnifiedTopology: true,
-});
-
-async function run(): Promise {
- const transaction = Sentry.startTransaction({
- name: 'Test Transaction',
- op: 'transaction',
- });
-
- Sentry.configureScope(scope => {
- scope.setSpan(transaction);
- });
-
- try {
- await client.connect();
-
- const database = client.db('admin');
- const collection = database.collection('movies');
-
- await collection.insertOne({ title: 'Rick and Morty' });
- await collection.findOne({ title: 'Back to the Future' });
- await collection.updateOne({ title: 'Back to the Future' }, { $set: { title: 'South Park' } });
- await collection.findOne({ title: 'South Park' });
-
- await collection.find({ title: 'South Park' }).toArray();
- } finally {
- if (transaction) transaction.finish();
- await client.close();
- }
-}
-
-void run();
diff --git a/packages/node-integration-tests/suites/tracing-new/auto-instrument/mongodb/test.ts b/packages/node-integration-tests/suites/tracing-new/auto-instrument/mongodb/test.ts
deleted file mode 100644
index 5664aac9422b..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/auto-instrument/mongodb/test.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import { MongoMemoryServer } from 'mongodb-memory-server-global';
-
-import { assertSentryTransaction, conditionalTest, TestEnv } from '../../../../utils';
-
-// This test can take longer.
-jest.setTimeout(15000);
-
-conditionalTest({ min: 12 })('MongoDB Test', () => {
- let mongoServer: MongoMemoryServer;
-
- beforeAll(async () => {
- mongoServer = await MongoMemoryServer.create();
- process.env.MONGO_URL = mongoServer.getUri();
- }, 10000);
-
- afterAll(async () => {
- if (mongoServer) {
- await mongoServer.stop();
- }
- });
-
- test('should auto-instrument `mongodb` package.', async () => {
- const env = await TestEnv.init(__dirname);
- const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });
-
- expect(envelope).toHaveLength(3);
-
- assertSentryTransaction(envelope[2], {
- transaction: 'Test Transaction',
- spans: [
- {
- data: {
- collectionName: 'movies',
- dbName: 'admin',
- namespace: 'admin.movies',
- doc: '{"title":"Rick and Morty"}',
- },
- description: 'insertOne',
- op: 'db',
- },
- {
- data: {
- collectionName: 'movies',
- dbName: 'admin',
- namespace: 'admin.movies',
- query: '{"title":"Back to the Future"}',
- },
- description: 'findOne',
- op: 'db',
- },
- {
- data: {
- collectionName: 'movies',
- dbName: 'admin',
- namespace: 'admin.movies',
- filter: '{"title":"Back to the Future"}',
- update: '{"$set":{"title":"South Park"}}',
- },
- description: 'updateOne',
- op: 'db',
- },
- {
- data: {
- collectionName: 'movies',
- dbName: 'admin',
- namespace: 'admin.movies',
- query: '{"title":"South Park"}',
- },
- description: 'findOne',
- op: 'db',
- },
- {
- data: {
- collectionName: 'movies',
- dbName: 'admin',
- namespace: 'admin.movies',
- query: '{"title":"South Park"}',
- },
- description: 'find',
- op: 'db',
- },
- ],
- });
- });
-});
diff --git a/packages/node-integration-tests/suites/tracing-new/auto-instrument/mysql/scenario.ts b/packages/node-integration-tests/suites/tracing-new/auto-instrument/mysql/scenario.ts
deleted file mode 100644
index 0f576cb793aa..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/auto-instrument/mysql/scenario.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import * as Sentry from '@sentry/node';
-import mysql from 'mysql';
-
-Sentry.init({
- dsn: 'https://public@dsn.ingest.sentry.io/1337',
- release: '1.0',
- tracesSampleRate: 1.0,
- integrations: [...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations()],
-});
-
-const connection = mysql.createConnection({
- user: 'root',
- password: 'docker',
-});
-
-connection.connect(function (err: unknown) {
- if (err) {
- return;
- }
-});
-
-const transaction = Sentry.startTransaction({
- op: 'transaction',
- name: 'Test Transaction',
-});
-
-Sentry.configureScope(scope => {
- scope.setSpan(transaction);
-});
-
-connection.query('SELECT 1 + 1 AS solution', function () {
- connection.query('SELECT NOW()', ['1', '2'], () => {
- if (transaction) transaction.finish();
- connection.end();
- });
-});
diff --git a/packages/node-integration-tests/suites/tracing-new/auto-instrument/mysql/test.ts b/packages/node-integration-tests/suites/tracing-new/auto-instrument/mysql/test.ts
deleted file mode 100644
index 3b96f2cafec0..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/auto-instrument/mysql/test.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { assertSentryTransaction, TestEnv } from '../../../../utils';
-
-test('should auto-instrument `mysql` package.', async () => {
- const env = await TestEnv.init(__dirname);
- const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });
-
- expect(envelope).toHaveLength(3);
-
- assertSentryTransaction(envelope[2], {
- transaction: 'Test Transaction',
- spans: [
- {
- description: 'SELECT 1 + 1 AS solution',
- op: 'db',
- },
-
- {
- description: 'SELECT NOW()',
- op: 'db',
- },
- ],
- });
-});
diff --git a/packages/node-integration-tests/suites/tracing-new/auto-instrument/pg/scenario.ts b/packages/node-integration-tests/suites/tracing-new/auto-instrument/pg/scenario.ts
deleted file mode 100644
index a7859fd562a3..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/auto-instrument/pg/scenario.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import * as Sentry from '@sentry/node';
-import pg from 'pg';
-
-Sentry.init({
- dsn: 'https://public@dsn.ingest.sentry.io/1337',
- release: '1.0',
- tracesSampleRate: 1.0,
- integrations: [...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations()],
-});
-
-const transaction = Sentry.startTransaction({
- op: 'transaction',
- name: 'Test Transaction',
-});
-
-Sentry.configureScope(scope => {
- scope.setSpan(transaction);
-});
-
-const client = new pg.Client();
-client.query('SELECT * FROM foo where bar ilike "baz%"', ['a', 'b'], () =>
- client.query('SELECT * FROM bazz', () => {
- client.query('SELECT NOW()', () => transaction.finish());
- }),
-);
diff --git a/packages/node-integration-tests/suites/tracing-new/auto-instrument/pg/test.ts b/packages/node-integration-tests/suites/tracing-new/auto-instrument/pg/test.ts
deleted file mode 100644
index edfa67cee9d7..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/auto-instrument/pg/test.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { assertSentryTransaction, TestEnv } from '../../../../utils';
-
-class PgClient {
- // https://node-postgres.com/api/client#clientquery
- public query(_text: unknown, values: unknown, callback?: () => void) {
- if (typeof callback === 'function') {
- callback();
- return;
- }
-
- if (typeof values === 'function') {
- values();
- return;
- }
-
- return Promise.resolve();
- }
-}
-
-beforeAll(() => {
- jest.mock('pg', () => {
- return {
- Client: PgClient,
- native: {
- Client: PgClient,
- },
- };
- });
-});
-
-test('should auto-instrument `pg` package.', async () => {
- const env = await TestEnv.init(__dirname);
- const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });
-
- expect(envelope).toHaveLength(3);
-
- assertSentryTransaction(envelope[2], {
- transaction: 'Test Transaction',
- spans: [
- {
- description: 'SELECT * FROM foo where bar ilike "baz%"',
- op: 'db',
- },
- {
- description: 'SELECT * FROM bazz',
- op: 'db',
- },
- {
- description: 'SELECT NOW()',
- op: 'db',
- },
- ],
- });
-});
diff --git a/packages/node-integration-tests/suites/tracing-new/prisma-orm/docker-compose.yml b/packages/node-integration-tests/suites/tracing-new/prisma-orm/docker-compose.yml
deleted file mode 100644
index 45caa4bb3179..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/prisma-orm/docker-compose.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-version: '3.9'
-
-services:
- db:
- image: postgres:13
- restart: always
- container_name: integration-tests-prisma
- ports:
- - '5433:5432'
- environment:
- POSTGRES_USER: prisma
- POSTGRES_PASSWORD: prisma
- POSTGRES_DB: tests
diff --git a/packages/node-integration-tests/suites/tracing-new/prisma-orm/package.json b/packages/node-integration-tests/suites/tracing-new/prisma-orm/package.json
deleted file mode 100644
index f8b24d7d0465..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/prisma-orm/package.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "name": "sentry-prisma-test",
- "version": "1.0.0",
- "description": "",
- "main": "index.js",
- "engines": {
- "node": ">=12"
- },
- "scripts": {
- "db-up": "docker-compose up -d",
- "generate": "prisma generate",
- "migrate": "prisma migrate dev -n sentry-test",
- "setup": "run-s --silent db-up generate migrate"
- },
- "keywords": [],
- "author": "",
- "license": "ISC",
- "dependencies": {
- "@prisma/client": "3.12.0",
- "prisma": "^3.12.0"
- }
-}
diff --git a/packages/node-integration-tests/suites/tracing-new/prisma-orm/prisma/migrations/migration_lock.toml b/packages/node-integration-tests/suites/tracing-new/prisma-orm/prisma/migrations/migration_lock.toml
deleted file mode 100644
index fbffa92c2bb7..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/prisma-orm/prisma/migrations/migration_lock.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-# Please do not edit this file manually
-# It should be added in your version-control system (i.e. Git)
-provider = "postgresql"
\ No newline at end of file
diff --git a/packages/node-integration-tests/suites/tracing-new/prisma-orm/prisma/migrations/sentry_test/migration.sql b/packages/node-integration-tests/suites/tracing-new/prisma-orm/prisma/migrations/sentry_test/migration.sql
deleted file mode 100644
index 8619aaceb2b0..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/prisma-orm/prisma/migrations/sentry_test/migration.sql
+++ /dev/null
@@ -1,12 +0,0 @@
--- CreateTable
-CREATE TABLE "User" (
- "id" SERIAL NOT NULL,
- "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
- "email" TEXT NOT NULL,
- "name" TEXT,
-
- CONSTRAINT "User_pkey" PRIMARY KEY ("id")
-);
-
--- CreateIndex
-CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
diff --git a/packages/node-integration-tests/suites/tracing-new/prisma-orm/prisma/schema.prisma b/packages/node-integration-tests/suites/tracing-new/prisma-orm/prisma/schema.prisma
deleted file mode 100644
index 4363c97738ee..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/prisma-orm/prisma/schema.prisma
+++ /dev/null
@@ -1,15 +0,0 @@
-datasource db {
- url = "postgresql://prisma:prisma@localhost:5433/tests"
- provider = "postgresql"
-}
-
-generator client {
- provider = "prisma-client-js"
-}
-
-model User {
- id Int @id @default(autoincrement())
- createdAt DateTime @default(now())
- email String @unique
- name String?
-}
diff --git a/packages/node-integration-tests/suites/tracing-new/prisma-orm/scenario.ts b/packages/node-integration-tests/suites/tracing-new/prisma-orm/scenario.ts
deleted file mode 100644
index 0eb40d9c83ee..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/prisma-orm/scenario.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-import { PrismaClient } from '@prisma/client';
-import * as Sentry from '@sentry/node';
-import { randomBytes } from 'crypto';
-
-const client = new PrismaClient();
-
-Sentry.init({
- dsn: 'https://public@dsn.ingest.sentry.io/1337',
- release: '1.0',
- tracesSampleRate: 1.0,
- integrations: [new Sentry.Integrations.Prisma({ client })],
-});
-
-async function run(): Promise {
- const transaction = Sentry.startTransaction({
- name: 'Test Transaction',
- op: 'transaction',
- });
-
- Sentry.configureScope(scope => {
- scope.setSpan(transaction);
- });
-
- try {
- await client.user.create({
- data: {
- name: 'Tilda',
- email: `tilda_${randomBytes(4).toString('hex')}@sentry.io`,
- },
- });
-
- await client.user.findMany();
-
- await client.user.deleteMany({
- where: {
- email: {
- contains: 'sentry.io',
- },
- },
- });
- } finally {
- if (transaction) transaction.finish();
- }
-}
-
-void run();
diff --git a/packages/node-integration-tests/suites/tracing-new/prisma-orm/setup.ts b/packages/node-integration-tests/suites/tracing-new/prisma-orm/setup.ts
deleted file mode 100755
index 3c40d12f7337..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/prisma-orm/setup.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { parseSemver } from '@sentry/utils';
-import { execSync } from 'child_process';
-
-const NODE_VERSION = parseSemver(process.versions.node);
-
-if (NODE_VERSION.major && NODE_VERSION.major < 12) {
- // eslint-disable-next-line no-console
- console.warn(`Skipping Prisma tests on Node: ${NODE_VERSION.major}`);
- process.exit(0);
-}
-
-try {
- execSync('yarn && yarn setup');
-} catch (_) {
- process.exit(1);
-}
diff --git a/packages/node-integration-tests/suites/tracing-new/prisma-orm/test.ts b/packages/node-integration-tests/suites/tracing-new/prisma-orm/test.ts
deleted file mode 100644
index e3393f5fe2f8..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/prisma-orm/test.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { assertSentryTransaction, conditionalTest, TestEnv } from '../../../utils';
-
-conditionalTest({ min: 12 })('Prisma ORM Integration', () => {
- test('should instrument Prisma client for tracing.', async () => {
- const env = await TestEnv.init(__dirname);
- const envelope = await env.getEnvelopeRequest({ envelopeType: 'transaction' });
-
- assertSentryTransaction(envelope[2], {
- transaction: 'Test Transaction',
- spans: [
- { description: 'User create', op: 'db.sql.prisma' },
- { description: 'User findMany', op: 'db.sql.prisma' },
- { description: 'User deleteMany', op: 'db.sql.prisma' },
- ],
- });
- });
-});
diff --git a/packages/node-integration-tests/suites/tracing-new/prisma-orm/yarn.lock b/packages/node-integration-tests/suites/tracing-new/prisma-orm/yarn.lock
deleted file mode 100644
index d228adebd621..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/prisma-orm/yarn.lock
+++ /dev/null
@@ -1,27 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
-"@prisma/client@3.12.0":
- version "3.12.0"
- resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.12.0.tgz#a0eb49ffea5c128dd11dffb896d7139a60073d12"
- integrity sha512-4NEQjUcWja/NVBvfuDFscWSk1/rXg3+wj+TSkqXCb1tKlx/bsUE00rxsvOvGg7VZ6lw1JFpGkwjwmsOIc4zvQw==
- dependencies:
- "@prisma/engines-version" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"
-
-"@prisma/engines-version@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980":
- version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"
- resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#829ca3d9d0d92555f44644606d4edfd45b2f5886"
- integrity sha512-o+jo8d7ZEiVpcpNWUDh3fj2uPQpBxl79XE9ih9nkogJbhw6P33274SHnqheedZ7PyvPIK/mvU8MLNYgetgXPYw==
-
-"@prisma/engines@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980":
- version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"
- resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#e52e364084c4d05278f62768047b788665e64a45"
- integrity sha512-zULjkN8yhzS7B3yeEz4aIym4E2w1ChrV12i14pht3ePFufvsAvBSoZ+tuXMvfSoNTgBS5E4bolRzLbMmbwkkMQ==
-
-prisma@^3.12.0:
- version "3.12.0"
- resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.12.0.tgz#9675e0e72407122759d3eadcb6d27cdccd3497bd"
- integrity sha512-ltCMZAx1i0i9xuPM692Srj8McC665h6E5RqJom999sjtVSccHSD8Z+HSdBN2183h9PJKvC5dapkn78dd0NWMBg==
- dependencies:
- "@prisma/engines" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980"
diff --git a/packages/node-integration-tests/suites/tracing-new/tracePropagationTargets/scenario.ts b/packages/node-integration-tests/suites/tracing-new/tracePropagationTargets/scenario.ts
deleted file mode 100644
index a6197e5ab743..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/tracePropagationTargets/scenario.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-import * as Sentry from '@sentry/node';
-import * as http from 'http';
-
-Sentry.addTracingExtensions();
-
-Sentry.init({
- dsn: 'https://public@dsn.ingest.sentry.io/1337',
- release: '1.0',
- tracesSampleRate: 1.0,
- tracePropagationTargets: [/\/v0/, 'v1'],
- integrations: [new Sentry.Integrations.Http({ tracing: true })],
-});
-
-const transaction = Sentry.startTransaction({ name: 'test_transaction' });
-
-Sentry.configureScope(scope => {
- scope.setSpan(transaction);
-});
-
-http.get('http://match-this-url.com/api/v0');
-http.get('http://match-this-url.com/api/v1');
-http.get('http://dont-match-this-url.com/api/v2');
-http.get('http://dont-match-this-url.com/api/v3');
-
-transaction.finish();
diff --git a/packages/node-integration-tests/suites/tracing-new/tracePropagationTargets/test.ts b/packages/node-integration-tests/suites/tracing-new/tracePropagationTargets/test.ts
deleted file mode 100644
index 1209c59da46a..000000000000
--- a/packages/node-integration-tests/suites/tracing-new/tracePropagationTargets/test.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import nock from 'nock';
-
-import { runScenario, TestEnv } from '../../../utils';
-
-test('HttpIntegration should instrument correct requests when tracePropagationTargets option is provided', async () => {
- const match1 = nock('http://match-this-url.com')
- .get('/api/v0')
- .matchHeader('baggage', val => typeof val === 'string')
- .matchHeader('sentry-trace', val => typeof val === 'string')
- .reply(200);
-
- const match2 = nock('http://match-this-url.com')
- .get('/api/v1')
- .matchHeader('baggage', val => typeof val === 'string')
- .matchHeader('sentry-trace', val => typeof val === 'string')
- .reply(200);
-
- const match3 = nock('http://dont-match-this-url.com')
- .get('/api/v2')
- .matchHeader('baggage', val => val === undefined)
- .matchHeader('sentry-trace', val => val === undefined)
- .reply(200);
-
- const match4 = nock('http://dont-match-this-url.com')
- .get('/api/v3')
- .matchHeader('baggage', val => val === undefined)
- .matchHeader('sentry-trace', val => val === undefined)
- .reply(200);
-
- const env = await TestEnv.init(__dirname);
- await runScenario(env.url);
-
- env.server.close();
- nock.cleanAll();
-
- await new Promise(resolve => env.server.close(resolve));
-
- expect(match1.isDone()).toBe(true);
- expect(match2.isDone()).toBe(true);
- expect(match3.isDone()).toBe(true);
- expect(match4.isDone()).toBe(true);
-});
diff --git a/packages/node/.eslintrc.js b/packages/node/.eslintrc.js
index 8e4b96002ae0..e15b119aa31e 100644
--- a/packages/node/.eslintrc.js
+++ b/packages/node/.eslintrc.js
@@ -6,6 +6,5 @@ module.exports = {
rules: {
'@sentry-internal/sdk/no-optional-chaining': 'off',
'@sentry-internal/sdk/no-nullish-coalescing': 'off',
- '@sentry-internal/sdk/no-unsupported-es6-methods': 'off',
},
};
diff --git a/packages/node/package.json b/packages/node/package.json
index fd197f539739..7386be3912bb 100644
--- a/packages/node/package.json
+++ b/packages/node/package.json
@@ -19,13 +19,13 @@
"@sentry/core": "7.44.2",
"@sentry/types": "7.44.2",
"@sentry/utils": "7.44.2",
- "@sentry-internal/tracing": "7.44.2",
"cookie": "^0.4.1",
"https-proxy-agent": "^5.0.0",
"lru_map": "^0.3.3",
"tslib": "^1.9.3"
},
"devDependencies": {
+ "@sentry/tracing": "7.44.2",
"@types/cookie": "0.3.2",
"@types/express": "^4.17.14",
"@types/lru-cache": "^5.1.0",
diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts
index d0d0ae7424be..0b3a925d775e 100644
--- a/packages/node/src/client.ts
+++ b/packages/node/src/client.ts
@@ -1,5 +1,5 @@
import type { Scope } from '@sentry/core';
-import { addTracingExtensions, BaseClient, SDK_VERSION, SessionFlusher } from '@sentry/core';
+import { BaseClient, SDK_VERSION, SessionFlusher } from '@sentry/core';
import type { Event, EventHint, Severity, SeverityLevel } from '@sentry/types';
import { logger, resolvedSyncPromise } from '@sentry/utils';
import * as os from 'os';
@@ -40,9 +40,6 @@ export class NodeClient extends BaseClient {
...options.transportOptions,
};
- // The Node client always supports tracing
- addTracingExtensions();
-
super(options);
}
diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts
index 7f0d923e4ae2..92d3cfcf835f 100644
--- a/packages/node/src/index.ts
+++ b/packages/node/src/index.ts
@@ -30,7 +30,6 @@ export {
captureMessage,
configureScope,
createTransport,
- getActiveTransaction,
getHubFromCarrier,
getCurrentHub,
Hub,
@@ -46,7 +45,6 @@ export {
setUser,
withScope,
} from '@sentry/core';
-export { autoDiscoverNodePerformanceMonitoringIntegrations } from './tracing';
export { NodeClient } from './client';
export { makeNodeTransport } from './transports';
@@ -59,12 +57,10 @@ import * as domain from 'domain';
import * as Handlers from './handlers';
import * as NodeIntegrations from './integrations';
-import * as TracingIntegrations from './tracing/integrations';
const INTEGRATIONS = {
...CoreIntegrations,
...NodeIntegrations,
- ...TracingIntegrations,
};
export { INTEGRATIONS as Integrations, Handlers };
diff --git a/packages/node/src/integrations/onuncaughtexception.ts b/packages/node/src/integrations/onuncaughtexception.ts
index b4f99b419fd4..2d10ae61d696 100644
--- a/packages/node/src/integrations/onuncaughtexception.ts
+++ b/packages/node/src/integrations/onuncaughtexception.ts
@@ -8,10 +8,6 @@ import { logAndExitProcess } from './utils/errorhandling';
type OnFatalErrorHandler = (firstError: Error, secondError?: Error) => void;
-type TaggedListener = NodeJS.UncaughtExceptionListener & {
- tag?: string;
-};
-
// CAREFUL: Please think twice before updating the way _options looks because the Next.js SDK depends on it in `index.server.ts`
interface OnUncaughtExceptionOptions {
// TODO(v8): Evaluate whether we should switch the default behaviour here.
@@ -99,20 +95,18 @@ export class OnUncaughtException implements Integration {
// exit behaviour of the SDK accordingly:
// - If other listeners are attached, do not exit.
// - If the only listener attached is ours, exit.
- const userProvidedListenersCount = (
- global.process.listeners('uncaughtException') as TaggedListener[]
- ).reduce((acc, listener) => {
- if (
- // There are 3 listeners we ignore:
- listener.name === 'domainUncaughtExceptionClear' || // as soon as we're using domains this listener is attached by node itself
- (listener.tag && listener.tag === 'sentry_tracingErrorCallback') || // the handler we register for tracing
- listener === this.handler // the handler we register in this integration
- ) {
- return acc;
- } else {
- return acc + 1;
- }
- }, 0);
+ const userProvidedListenersCount = global.process
+ .listeners('uncaughtException')
+ .reduce((acc, listener) => {
+ if (
+ listener.name === 'domainUncaughtExceptionClear' || // as soon as we're using domains this listener is attached by node itself
+ listener === this.handler // filter the handler we registered ourselves)
+ ) {
+ return acc;
+ } else {
+ return acc + 1;
+ }
+ }, 0);
const processWouldExit = userProvidedListenersCount === 0;
const shouldApplyFatalHandlingLogic = this._options.exitEvenIfOtherHandlersAreRegistered || processWouldExit;
diff --git a/packages/node/src/tracing/index.ts b/packages/node/src/tracing/index.ts
deleted file mode 100644
index 15c4e2889b3f..000000000000
--- a/packages/node/src/tracing/index.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { lazyLoadedNodePerformanceMonitoringIntegrations } from '@sentry-internal/tracing';
-import type { Integration } from '@sentry/types';
-import { logger } from '@sentry/utils';
-
-/**
- * Automatically detects and returns integrations that will work with your dependencies.
- */
-export function autoDiscoverNodePerformanceMonitoringIntegrations(): Integration[] {
- const loadedIntegrations = lazyLoadedNodePerformanceMonitoringIntegrations
- .map(tryLoad => {
- try {
- return tryLoad();
- } catch (_) {
- return undefined;
- }
- })
- .filter(integration => !!integration) as Integration[];
-
- if (loadedIntegrations.length === 0) {
- logger.warn('Performance monitoring integrations could not be automatically loaded.');
- }
-
- return loadedIntegrations;
-}
diff --git a/packages/node/src/tracing/integrations.ts b/packages/node/src/tracing/integrations.ts
deleted file mode 100644
index a37bf6bfd494..000000000000
--- a/packages/node/src/tracing/integrations.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { Apollo, Express, GraphQL, Mongo, Mysql, Postgres, Prisma } from '@sentry-internal/tracing';
diff --git a/packages/node/test/handlers.test.ts b/packages/node/test/handlers.test.ts
index 028cdff98af1..03cfba80ade4 100644
--- a/packages/node/test/handlers.test.ts
+++ b/packages/node/test/handlers.test.ts
@@ -1,5 +1,5 @@
import * as sentryCore from '@sentry/core';
-import { Transaction } from '@sentry/core';
+import { Transaction } from '@sentry/tracing';
import type { Event } from '@sentry/types';
import { SentryError } from '@sentry/utils';
import * as http from 'http';
diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts
index 7d9a4e45226b..446aa4ec0f82 100644
--- a/packages/node/test/integrations/http.test.ts
+++ b/packages/node/test/integrations/http.test.ts
@@ -1,8 +1,9 @@
-import type { Span, Transaction } from '@sentry/core';
import * as sentryCore from '@sentry/core';
-import { addTracingExtensions, Hub } from '@sentry/core';
+import { Hub } from '@sentry/core';
+import type { Span, Transaction } from '@sentry/tracing';
+import { addExtensionMethods, TRACEPARENT_REGEXP } from '@sentry/tracing';
import type { TransactionContext } from '@sentry/types';
-import { logger, parseSemver, TRACEPARENT_REGEXP } from '@sentry/utils';
+import { logger, parseSemver } from '@sentry/utils';
import * as http from 'http';
import * as https from 'https';
import * as HttpsProxyAgent from 'https-proxy-agent';
@@ -33,7 +34,7 @@ describe('tracing', () => {
...customOptions,
});
const hub = new Hub(new NodeClient(options));
- addTracingExtensions();
+ addExtensionMethods();
hub.configureScope(scope =>
scope.setUser({
@@ -226,7 +227,7 @@ describe('tracing', () => {
}
function createTransactionAndPutOnScope(hub: Hub) {
- addTracingExtensions();
+ addExtensionMethods();
const transaction = hub.startTransaction({ name: 'dogpark' });
hub.getScope()?.setSpan(transaction);
return transaction;
diff --git a/packages/opentelemetry-node/README.md b/packages/opentelemetry-node/README.md
index b75ca0ab0ef8..551684a75c26 100644
--- a/packages/opentelemetry-node/README.md
+++ b/packages/opentelemetry-node/README.md
@@ -66,9 +66,10 @@ const sdk = new opentelemetry.NodeSDK({
// Sentry config
spanProcessor: new SentrySpanProcessor(),
- textMapPropagator: new SentryPropagator(),
});
+otelApi.propagation.setGlobalPropagator(new SentryPropagator());
+
sdk.start();
```
diff --git a/packages/opentelemetry-node/package.json b/packages/opentelemetry-node/package.json
index b19a608ef2e3..84391b502c63 100644
--- a/packages/opentelemetry-node/package.json
+++ b/packages/opentelemetry-node/package.json
@@ -17,6 +17,7 @@
},
"dependencies": {
"@sentry/core": "7.44.2",
+ "@sentry/tracing": "7.44.2",
"@sentry/types": "7.44.2",
"@sentry/utils": "7.44.2"
},
diff --git a/packages/opentelemetry-node/src/index.ts b/packages/opentelemetry-node/src/index.ts
index 752c2cfaa0aa..501c93432b44 100644
--- a/packages/opentelemetry-node/src/index.ts
+++ b/packages/opentelemetry-node/src/index.ts
@@ -1,2 +1,4 @@
+import '@sentry/tracing';
+
export { SentrySpanProcessor } from './spanprocessor';
export { SentryPropagator } from './propagator';
diff --git a/packages/opentelemetry-node/src/spanprocessor.ts b/packages/opentelemetry-node/src/spanprocessor.ts
index 773c68ebf1ff..08d14e9fa671 100644
--- a/packages/opentelemetry-node/src/spanprocessor.ts
+++ b/packages/opentelemetry-node/src/spanprocessor.ts
@@ -2,7 +2,8 @@ import type { Context } from '@opentelemetry/api';
import { SpanKind, trace } from '@opentelemetry/api';
import type { Span as OtelSpan, SpanProcessor as OtelSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
-import { addGlobalEventProcessor, addTracingExtensions, getCurrentHub, Transaction } from '@sentry/core';
+import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core';
+import { Transaction } from '@sentry/tracing';
import type { DynamicSamplingContext, Span as SentrySpan, TraceparentData, TransactionContext } from '@sentry/types';
import { isString, logger } from '@sentry/utils';
@@ -22,8 +23,6 @@ export const SENTRY_SPAN_PROCESSOR_MAP: Map =
*/
export class SentrySpanProcessor implements OtelSpanProcessor {
public constructor() {
- addTracingExtensions();
-
addGlobalEventProcessor(event => {
const otelSpan = trace && trace.getActiveSpan && (trace.getActiveSpan() as OtelSpan | undefined);
if (!otelSpan) {
diff --git a/packages/opentelemetry-node/src/utils/map-otel-status.ts b/packages/opentelemetry-node/src/utils/map-otel-status.ts
index 8fdc0e09ac8b..968150852e6e 100644
--- a/packages/opentelemetry-node/src/utils/map-otel-status.ts
+++ b/packages/opentelemetry-node/src/utils/map-otel-status.ts
@@ -1,6 +1,6 @@
import type { Span as OtelSpan } from '@opentelemetry/sdk-trace-base';
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
-import type { SpanStatusType as SentryStatus } from '@sentry/core';
+import type { SpanStatusType as SentryStatus } from '@sentry/tracing';
// canonicalCodesHTTPMap maps some HTTP codes to Sentry's span statuses. See possible mapping in https://develop.sentry.dev/sdk/event-payloads/span/
const canonicalCodesHTTPMap: Record = {
diff --git a/packages/opentelemetry-node/test/propagator.test.ts b/packages/opentelemetry-node/test/propagator.test.ts
index d5222e3103d4..f1914653dd0d 100644
--- a/packages/opentelemetry-node/test/propagator.test.ts
+++ b/packages/opentelemetry-node/test/propagator.test.ts
@@ -7,7 +7,8 @@ import {
TraceFlags,
} from '@opentelemetry/api';
import { suppressTracing } from '@opentelemetry/core';
-import { addTracingExtensions, Hub, makeMain, Transaction } from '@sentry/core';
+import { Hub, makeMain } from '@sentry/core';
+import { addExtensionMethods, Transaction } from '@sentry/tracing';
import type { TransactionContext } from '@sentry/types';
import {
@@ -20,7 +21,7 @@ import { SentryPropagator } from '../src/propagator';
import { SENTRY_SPAN_PROCESSOR_MAP } from '../src/spanprocessor';
beforeAll(() => {
- addTracingExtensions();
+ addExtensionMethods();
});
describe('SentryPropagator', () => {
diff --git a/packages/opentelemetry-node/test/spanprocessor.test.ts b/packages/opentelemetry-node/test/spanprocessor.test.ts
index 94abd327314c..4e3c975d00cf 100644
--- a/packages/opentelemetry-node/test/spanprocessor.test.ts
+++ b/packages/opentelemetry-node/test/spanprocessor.test.ts
@@ -4,9 +4,10 @@ import { Resource } from '@opentelemetry/resources';
import type { Span as OtelSpan } from '@opentelemetry/sdk-trace-base';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { SemanticAttributes, SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
-import type { SpanStatusType } from '@sentry/core';
-import { addTracingExtensions, createTransport, Hub, makeMain, Span as SentrySpan, Transaction } from '@sentry/core';
+import { createTransport, Hub, makeMain } from '@sentry/core';
import { NodeClient } from '@sentry/node';
+import type { SpanStatusType } from '@sentry/tracing';
+import { addExtensionMethods, Span as SentrySpan, Transaction } from '@sentry/tracing';
import { resolvedSyncPromise } from '@sentry/utils';
import { SENTRY_SPAN_PROCESSOR_MAP, SentrySpanProcessor } from '../src/spanprocessor';
@@ -23,7 +24,7 @@ const DEFAULT_NODE_CLIENT_OPTIONS = {
// Integration Test of SentrySpanProcessor
beforeAll(() => {
- addTracingExtensions();
+ addExtensionMethods();
});
describe('SentrySpanProcessor', () => {
diff --git a/packages/overhead-metrics/.eslintrc.cjs b/packages/overhead-metrics/.eslintrc.cjs
index 8046df7df92c..1cbdd66daa88 100644
--- a/packages/overhead-metrics/.eslintrc.cjs
+++ b/packages/overhead-metrics/.eslintrc.cjs
@@ -10,7 +10,6 @@ module.exports = {
'import/no-unresolved': 'off',
'@sentry-internal/sdk/no-optional-chaining': 'off',
'@sentry-internal/sdk/no-nullish-coalescing': 'off',
- '@sentry-internal/sdk/no-unsupported-es6-methods': 'off',
'jsdoc/require-jsdoc': 'off',
},
},
diff --git a/packages/react/src/errorboundary.tsx b/packages/react/src/errorboundary.tsx
index 96a88cf31e73..1553028195a2 100644
--- a/packages/react/src/errorboundary.tsx
+++ b/packages/react/src/errorboundary.tsx
@@ -66,25 +66,6 @@ const INITIAL_STATE = {
eventId: null,
};
-function setCause(error: Error & { cause?: Error }, cause: Error): void {
- const seenErrors = new WeakMap();
-
- function recurse(error: Error & { cause?: Error }, cause: Error): void {
- // If we've already seen the error, there is a recursive loop somewhere in the error's
- // cause chain. Let's just bail out then to prevent a stack overflow.
- if (seenErrors.has(error)) {
- return;
- }
- if (error.cause) {
- seenErrors.set(error, true);
- return recurse(error.cause, cause);
- }
- error.cause = cause;
- }
-
- recurse(error, cause);
-}
-
/**
* A ErrorBoundary component that logs errors to Sentry. Requires React >= 16.
* NOTE: If you are a Sentry user, and you are seeing this stack frame, it means the
@@ -112,7 +93,7 @@ class ErrorBoundary extends React.Component;
}
-interface TestAppProps extends ErrorBoundaryProps {
- errorComp?: JSX.Element;
-}
-
-const TestApp: React.FC = ({ children, errorComp, ...props }) => {
- // eslint-disable-next-line no-param-reassign
- const customErrorComp = errorComp || ;
+const TestApp: React.FC = ({ children, ...props }) => {
const [isError, setError] = React.useState(false);
return (
= ({ children, errorComp, ...props }) => {
}
}}
>
- {isError ? customErrorComp : children}
+ {isError ? : children}
} onError={mockOnError} errorComp={}>
- children
- ,
- );
-
- expect(mockOnError).toHaveBeenCalledTimes(0);
- expect(mockCaptureException).toHaveBeenCalledTimes(0);
-
- const btn = screen.getByTestId('errorBtn');
- fireEvent.click(btn);
-
- expect(mockCaptureException).toHaveBeenCalledTimes(1);
- expect(mockCaptureException).toHaveBeenLastCalledWith(expect.any(Error), {
- contexts: { react: { componentStack: expect.any(String) } },
- });
-
- expect(mockOnError.mock.calls[0][0]).toEqual(mockCaptureException.mock.calls[0][0]);
-
- const thirdError = mockCaptureException.mock.calls[0][0];
- const secondError = thirdError.cause;
- const firstError = secondError.cause;
- const cause = firstError.cause;
- expect(cause.stack).toEqual(mockCaptureException.mock.calls[0][1].contexts.react.componentStack);
- expect(cause.name).toContain('React ErrorBoundary');
- expect(cause.message).toEqual(thirdError.message);
- });
-
- it('handles when `error.cause` is recursive', () => {
- const mockOnError = jest.fn();
-
- function CustomBam(): JSX.Element {
- const firstError = new Error('bam');
- const secondError = new Error('bam2');
- // @ts-ignore Need to set cause on error
- firstError.cause = secondError;
- // @ts-ignore Need to set cause on error
- secondError.cause = firstError;
- throw firstError;
- }
-
- render(
- You have hit an error} onError={mockOnError} errorComp={}>
- children
- ,
- );
-
- expect(mockOnError).toHaveBeenCalledTimes(0);
- expect(mockCaptureException).toHaveBeenCalledTimes(0);
-
- const btn = screen.getByTestId('errorBtn');
- fireEvent.click(btn);
-
- expect(mockCaptureException).toHaveBeenCalledTimes(1);
- expect(mockCaptureException).toHaveBeenLastCalledWith(expect.any(Error), {
- contexts: { react: { componentStack: expect.any(String) } },
- });
-
- expect(mockOnError.mock.calls[0][0]).toEqual(mockCaptureException.mock.calls[0][0]);
-
- const error = mockCaptureException.mock.calls[0][0];
- const cause = error.cause;
- // We need to make sure that recursive error.cause does not cause infinite loop
- expect(cause.stack).not.toEqual(mockCaptureException.mock.calls[0][1].contexts.react.componentStack);
- expect(cause.name).not.toContain('React ErrorBoundary');
- });
-
it('calls `beforeCapture()` when an error occurs', () => {
const mockBeforeCapture = jest.fn();
diff --git a/packages/remix/package.json b/packages/remix/package.json
index a2d68a4e0c38..d08489a442c9 100644
--- a/packages/remix/package.json
+++ b/packages/remix/package.json
@@ -25,6 +25,7 @@
"@sentry/integrations": "7.44.2",
"@sentry/node": "7.44.2",
"@sentry/react": "7.44.2",
+ "@sentry/tracing": "7.44.2",
"@sentry/types": "7.44.2",
"@sentry/utils": "7.44.2",
"@sentry/webpack-plugin": "1.19.0",
diff --git a/packages/remix/src/index.client.tsx b/packages/remix/src/index.client.tsx
index 5c76ee4907bf..22d9ce73fe82 100644
--- a/packages/remix/src/index.client.tsx
+++ b/packages/remix/src/index.client.tsx
@@ -1,11 +1,14 @@
/* eslint-disable import/export */
-import { configureScope, init as reactInit } from '@sentry/react';
+import { configureScope, init as reactInit, Integrations } from '@sentry/react';
import { buildMetadata } from './utils/metadata';
import type { RemixOptions } from './utils/remixOptions';
export { remixRouterInstrumentation, withSentry } from './performance/client';
+export { BrowserTracing } from '@sentry/tracing';
export * from '@sentry/react';
+export { Integrations };
+
export function init(options: RemixOptions): void {
buildMetadata(options, ['remix', 'react']);
options.environment = options.environment || process.env.NODE_ENV;
diff --git a/packages/remix/src/index.server.ts b/packages/remix/src/index.server.ts
index 21cfc20b17ab..b2ad73866fde 100644
--- a/packages/remix/src/index.server.ts
+++ b/packages/remix/src/index.server.ts
@@ -8,6 +8,7 @@ import type { RemixOptions } from './utils/remixOptions';
export { ErrorBoundary, withErrorBoundary } from '@sentry/react';
export { remixRouterInstrumentation, withSentry } from './performance/client';
+export { BrowserTracing, Integrations } from '@sentry/tracing';
export * from '@sentry/node';
export { wrapExpressCreateRequestHandler } from './utils/serverAdapters/express';
diff --git a/packages/remix/src/utils/instrumentServer.ts b/packages/remix/src/utils/instrumentServer.ts
index 825198426eec..43e4d8cd1bf0 100644
--- a/packages/remix/src/utils/instrumentServer.ts
+++ b/packages/remix/src/utils/instrumentServer.ts
@@ -1,7 +1,8 @@
/* eslint-disable max-lines */
-import { getActiveTransaction, hasTracingEnabled } from '@sentry/core';
+import { hasTracingEnabled } from '@sentry/core';
import type { Hub } from '@sentry/node';
import { captureException, getCurrentHub } from '@sentry/node';
+import { getActiveTransaction } from '@sentry/tracing';
import type { Transaction, TransactionSource, WrappedFunction } from '@sentry/types';
import {
addExceptionMechanism,
diff --git a/packages/remix/test/index.server.test.ts b/packages/remix/test/index.server.test.ts
index ec1610aee400..f4d4f9c95fe0 100644
--- a/packages/remix/test/index.server.test.ts
+++ b/packages/remix/test/index.server.test.ts
@@ -2,7 +2,7 @@ import * as SentryNode from '@sentry/node';
import { getCurrentHub } from '@sentry/node';
import { GLOBAL_OBJ } from '@sentry/utils';
-import { init, Integrations } from '../src/index.server';
+import { init } from '../src/index.server';
const nodeInit = jest.spyOn(SentryNode, 'init');
@@ -57,9 +57,4 @@ describe('Server init()', () => {
// @ts-ignore need access to protected _tags attribute
expect(currentScope._tags).toEqual({ runtime: 'node' });
});
-
- it('has both node and tracing integrations', () => {
- expect(Integrations.Apollo).not.toBeUndefined();
- expect(Integrations.Http).not.toBeUndefined();
- });
});
diff --git a/packages/replay/.eslintrc.js b/packages/replay/.eslintrc.js
index da006cf432a2..e4101e557b26 100644
--- a/packages/replay/.eslintrc.js
+++ b/packages/replay/.eslintrc.js
@@ -7,8 +7,21 @@ module.exports = {
extends: ['../../.eslintrc.js'],
overrides: [
{
- files: ['src/**/*.ts'],
- rules: {},
+ files: ['worker/**/*.ts'],
+ parserOptions: {
+ // TODO: figure out if we need a worker-specific tsconfig
+ project: ['tsconfig.worker.json'],
+ },
+ rules: {
+ // We cannot use backticks, as that conflicts with the stringified worker
+ 'prefer-template': 'off',
+ },
+ },
+ {
+ files: ['src/worker/**/*.js'],
+ parserOptions: {
+ sourceType: 'module',
+ },
},
{
files: ['jest.setup.ts', 'jest.config.ts'],
diff --git a/packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts b/packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts
index e9ad5d9ea209..0ee72f9edc9c 100644
--- a/packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts
+++ b/packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts
@@ -51,7 +51,7 @@ export function handleNetworkBreadcrumbs(replay: ReplayContainer): void {
};
if (client && client.on) {
- client.on('beforeAddBreadcrumb', (breadcrumb, hint) => beforeAddNetworkBreadcrumb(options, breadcrumb, hint));
+ client.on('beforeAddBreadcrumb', (breadcrumb, hint) => handleNetworkBreadcrumb(options, breadcrumb, hint));
} else {
// Fallback behavior
addInstrumentationHandler('fetch', handleFetchSpanListener(replay));
@@ -63,7 +63,7 @@ export function handleNetworkBreadcrumbs(replay: ReplayContainer): void {
}
/** just exported for tests */
-export function beforeAddNetworkBreadcrumb(
+export function handleNetworkBreadcrumb(
options: ExtendedNetworkBreadcrumbsOptions,
breadcrumb: Breadcrumb,
hint?: BreadcrumbHint,
@@ -74,76 +74,27 @@ export function beforeAddNetworkBreadcrumb(
try {
if (_isXhrBreadcrumb(breadcrumb) && _isXhrHint(hint)) {
- _handleXhrBreadcrumb(breadcrumb, hint, options);
+ // Enriches the breadcrumb overall
+ _enrichXhrBreadcrumb(breadcrumb, hint, options);
+
+ // Create a replay performance entry from this breadcrumb
+ const result = _makeNetworkReplayBreadcrumb('resource.xhr', breadcrumb, hint);
+ addNetworkBreadcrumb(options.replay, result);
}
if (_isFetchBreadcrumb(breadcrumb) && _isFetchHint(hint)) {
- // This has to be sync, as we need to ensure the breadcrumb is enriched in the same tick
- // Because the hook runs synchronously, and the breadcrumb is afterwards passed on
- // So any async mutations to it will not be reflected in the final breadcrumb
+ // Enriches the breadcrumb overall
_enrichFetchBreadcrumb(breadcrumb, hint, options);
- void _handleFetchBreadcrumb(breadcrumb, hint, options);
+ // Create a replay performance entry from this breadcrumb
+ const result = _makeNetworkReplayBreadcrumb('resource.fetch', breadcrumb, hint);
+ addNetworkBreadcrumb(options.replay, result);
}
} catch (e) {
__DEBUG_BUILD__ && logger.warn('Error when enriching network breadcrumb');
}
}
-function _handleXhrBreadcrumb(
- breadcrumb: Breadcrumb & { data: XhrBreadcrumbData },
- hint: XhrHint,
- options: ExtendedNetworkBreadcrumbsOptions,
-): void {
- // Enriches the breadcrumb overall
- _enrichXhrBreadcrumb(breadcrumb, hint, options);
-
- // Create a replay performance entry from this breadcrumb
- const result = _makeNetworkReplayBreadcrumb('resource.xhr', breadcrumb, hint);
- addNetworkBreadcrumb(options.replay, result);
-}
-
-async function _handleFetchBreadcrumb(
- breadcrumb: Breadcrumb & { data: FetchBreadcrumbData },
- hint: FetchHint,
- options: ExtendedNetworkBreadcrumbsOptions,
-): Promise {
- const fullBreadcrumb = await _parseFetchResponse(breadcrumb, hint, options);
-
- // Create a replay performance entry from this breadcrumb
- const result = _makeNetworkReplayBreadcrumb('resource.fetch', fullBreadcrumb, hint);
- addNetworkBreadcrumb(options.replay, result);
-}
-
-// This does async operations on the breadcrumb for replay
-async function _parseFetchResponse(
- breadcrumb: Breadcrumb & { data: FetchBreadcrumbData },
- hint: FetchBreadcrumbHint,
- options: ExtendedNetworkBreadcrumbsOptions,
-): Promise {
- if (breadcrumb.data.response_body_size || !hint.response) {
- return breadcrumb;
- }
-
- // If no Content-Length header exists, we try to get the size from the response body
- try {
- // We have to clone this, as the body can only be read once
- const response = (hint.response as Response).clone();
- const body = await response.text();
-
- if (body.length) {
- return {
- ...breadcrumb,
- data: { ...breadcrumb.data, response_body_size: getBodySize(body, options.textEncoder) },
- };
- }
- } catch {
- // just ignore if something fails here
- }
-
- return breadcrumb;
-}
-
function _makeNetworkReplayBreadcrumb(
type: string,
breadcrumb: Breadcrumb & { data: FetchBreadcrumbData | XhrBreadcrumbData },
diff --git a/packages/replay/src/util/addGlobalListeners.ts b/packages/replay/src/util/addGlobalListeners.ts
index 46ba18bb9ed3..fc68f322d090 100644
--- a/packages/replay/src/util/addGlobalListeners.ts
+++ b/packages/replay/src/util/addGlobalListeners.ts
@@ -1,6 +1,5 @@
import type { BaseClient } from '@sentry/core';
import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core';
-import type { Client, DynamicSamplingContext } from '@sentry/types';
import { addInstrumentationHandler } from '@sentry/utils';
import { handleAfterSendEvent } from '../coreHandlers/handleAfterSendEvent';
@@ -26,23 +25,15 @@ export function addGlobalListeners(replay: ReplayContainer): void {
addInstrumentationHandler('history', handleHistorySpanListener(replay));
handleNetworkBreadcrumbs(replay);
+ // If a custom client has no hooks yet, we continue to use the "old" implementation
+ const hasHooks = !!(client && client.on);
+
// Tag all (non replay) events that get sent to Sentry with the current
// replay ID so that we can reference them later in the UI
- addGlobalEventProcessor(handleGlobalEventListener(replay, !hasHooks(client)));
+ addGlobalEventProcessor(handleGlobalEventListener(replay, !hasHooks));
- // If a custom client has no hooks yet, we continue to use the "old" implementation
- if (hasHooks(client)) {
- client.on('afterSendEvent', handleAfterSendEvent(replay));
- client.on('createDsc', (dsc: DynamicSamplingContext) => {
- const replayId = replay.getSessionId();
- if (replayId) {
- dsc.replay_id = replayId;
- }
- });
+ if (hasHooks) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (client as BaseClient).on('afterSendEvent', handleAfterSendEvent(replay));
}
}
-
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-function hasHooks(client: Client | undefined): client is BaseClient {
- return !!(client && client.on);
-}
diff --git a/packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts b/packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts
index 1a1c5ac13d6b..d0a7b5cedca1 100644
--- a/packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts
+++ b/packages/replay/test/unit/coreHandlers/handleNetworkBreadcrumbs.test.ts
@@ -9,8 +9,8 @@ import { TextEncoder } from 'util';
import { BASE_TIMESTAMP } from '../..';
import {
- beforeAddNetworkBreadcrumb,
getBodySize,
+ handleNetworkBreadcrumb,
parseContentSizeHeader,
} from '../../../src/coreHandlers/handleNetworkBreadcrumbs';
import type { EventBufferArray } from '../../../src/eventBuffer/EventBufferArray';
@@ -78,7 +78,7 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
});
});
- describe('beforeAddNetworkBreadcrumb()', () => {
+ describe('handleNetworkBreadcrumb()', () => {
let options: {
replay: ReplayContainer;
textEncoder: TextEncoderInternal;
@@ -98,7 +98,7 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
it('ignores breadcrumb without data', () => {
const breadcrumb: Breadcrumb = {};
const hint: BreadcrumbHint = {};
- beforeAddNetworkBreadcrumb(options, breadcrumb, hint);
+ handleNetworkBreadcrumb(options, breadcrumb, hint);
expect(breadcrumb).toEqual({});
expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([]);
@@ -110,7 +110,7 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
data: {},
};
const hint: BreadcrumbHint = {};
- beforeAddNetworkBreadcrumb(options, breadcrumb, hint);
+ handleNetworkBreadcrumb(options, breadcrumb, hint);
expect(breadcrumb).toEqual({
category: 'foo',
@@ -138,7 +138,7 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
startTimestamp: BASE_TIMESTAMP + 1000,
endTimestamp: BASE_TIMESTAMP + 2000,
};
- beforeAddNetworkBreadcrumb(options, breadcrumb, hint);
+ handleNetworkBreadcrumb(options, breadcrumb, hint);
expect(breadcrumb).toEqual({
category: 'xhr',
@@ -192,7 +192,7 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
startTimestamp: BASE_TIMESTAMP + 1000,
endTimestamp: BASE_TIMESTAMP + 2000,
};
- beforeAddNetworkBreadcrumb(options, breadcrumb, hint);
+ handleNetworkBreadcrumb(options, breadcrumb, hint);
expect(breadcrumb).toEqual({
category: 'xhr',
@@ -246,7 +246,7 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
startTimestamp: BASE_TIMESTAMP + 1000,
endTimestamp: BASE_TIMESTAMP + 2000,
};
- beforeAddNetworkBreadcrumb(options, breadcrumb, hint);
+ handleNetworkBreadcrumb(options, breadcrumb, hint);
expect(breadcrumb).toEqual({
category: 'fetch',
@@ -260,7 +260,6 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
});
jest.runAllTimers();
- await Promise.resolve();
expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([
{
@@ -306,7 +305,7 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
startTimestamp: BASE_TIMESTAMP + 1000,
endTimestamp: BASE_TIMESTAMP + 2000,
};
- beforeAddNetworkBreadcrumb(options, breadcrumb, hint);
+ handleNetworkBreadcrumb(options, breadcrumb, hint);
expect(breadcrumb).toEqual({
category: 'fetch',
@@ -317,7 +316,6 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
});
jest.runAllTimers();
- await Promise.resolve();
expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([
{
@@ -338,63 +336,5 @@ describe('Unit | coreHandlers | handleNetworkBreadcrumbs', () => {
},
]);
});
-
- it('parses fetch response body if necessary', async () => {
- const breadcrumb: Breadcrumb = {
- category: 'fetch',
- data: {
- url: 'https://example.com',
- status_code: 200,
- },
- };
-
- const mockResponse = {
- headers: {
- get: () => '',
- },
- clone: () => mockResponse,
- text: () => Promise.resolve('test response'),
- } as unknown as Response;
-
- const hint: FetchBreadcrumbHint = {
- input: [],
- response: mockResponse,
- startTimestamp: BASE_TIMESTAMP + 1000,
- endTimestamp: BASE_TIMESTAMP + 2000,
- };
- beforeAddNetworkBreadcrumb(options, breadcrumb, hint);
-
- expect(breadcrumb).toEqual({
- category: 'fetch',
- data: {
- status_code: 200,
- url: 'https://example.com',
- },
- });
-
- await Promise.resolve();
- jest.runAllTimers();
- await Promise.resolve();
-
- expect((options.replay.eventBuffer as EventBufferArray).events).toEqual([
- {
- type: 5,
- timestamp: (BASE_TIMESTAMP + 1000) / 1000,
- data: {
- tag: 'performanceSpan',
- payload: {
- data: {
- statusCode: 200,
- responseBodySize: 13,
- },
- description: 'https://example.com',
- endTimestamp: (BASE_TIMESTAMP + 2000) / 1000,
- op: 'resource.fetch',
- startTimestamp: (BASE_TIMESTAMP + 1000) / 1000,
- },
- },
- },
- ]);
- });
});
});
diff --git a/packages/serverless/package.json b/packages/serverless/package.json
index 522d2419814c..e68ec304dbe3 100644
--- a/packages/serverless/package.json
+++ b/packages/serverless/package.json
@@ -17,6 +17,7 @@
},
"dependencies": {
"@sentry/node": "7.44.2",
+ "@sentry/tracing": "7.44.2",
"@sentry/types": "7.44.2",
"@sentry/utils": "7.44.2",
"@types/aws-lambda": "^8.10.62",
diff --git a/packages/serverless/scripts/buildLambdaLayer.ts b/packages/serverless/scripts/buildLambdaLayer.ts
index 459560a660fe..c7e2199aedbb 100644
--- a/packages/serverless/scripts/buildLambdaLayer.ts
+++ b/packages/serverless/scripts/buildLambdaLayer.ts
@@ -17,7 +17,7 @@ async function buildLambdaLayer(): Promise {
// Create the main SDK bundle
// TODO: Check if we can get rid of this, after the lerna 6/nx update??
await ensureBundleBuildPrereqs({
- dependencies: ['@sentry/utils', '@sentry/hub', '@sentry/core', '@sentry/node'],
+ dependencies: ['@sentry/utils', '@sentry/hub', '@sentry/core', '@sentry/tracing', '@sentry/node'],
});
run('yarn rollup --config rollup.aws.config.js');
diff --git a/packages/serverless/src/awslambda.ts b/packages/serverless/src/awslambda.ts
index ccfc1e191024..29dd10da6602 100644
--- a/packages/serverless/src/awslambda.ts
+++ b/packages/serverless/src/awslambda.ts
@@ -2,15 +2,9 @@
import type { Scope } from '@sentry/node';
import * as Sentry from '@sentry/node';
import { captureException, captureMessage, flush, getCurrentHub, withScope } from '@sentry/node';
+import { extractTraceparentData } from '@sentry/tracing';
import type { Integration } from '@sentry/types';
-import {
- baggageHeaderToDynamicSamplingContext,
- dsnFromString,
- dsnToString,
- extractTraceparentData,
- isString,
- logger,
-} from '@sentry/utils';
+import { baggageHeaderToDynamicSamplingContext, dsnFromString, dsnToString, isString, logger } from '@sentry/utils';
// NOTE: I have no idea how to fix this right now, and don't want to waste more time, as it builds just fine — Kamil
// eslint-disable-next-line import/no-unresolved
import type { Context, Handler } from 'aws-lambda';
diff --git a/packages/serverless/src/gcpfunction/http.ts b/packages/serverless/src/gcpfunction/http.ts
index 8892353fd4bf..fbe5882f549a 100644
--- a/packages/serverless/src/gcpfunction/http.ts
+++ b/packages/serverless/src/gcpfunction/http.ts
@@ -1,8 +1,8 @@
import type { AddRequestDataToEventOptions } from '@sentry/node';
import { captureException, flush, getCurrentHub } from '@sentry/node';
+import { extractTraceparentData } from '@sentry/tracing';
import {
baggageHeaderToDynamicSamplingContext,
- extractTraceparentData,
isString,
isThenable,
logger,
diff --git a/packages/serverless/src/index.ts b/packages/serverless/src/index.ts
index e0513a53cd3e..bd552060c6dd 100644
--- a/packages/serverless/src/index.ts
+++ b/packages/serverless/src/index.ts
@@ -20,7 +20,6 @@ export {
captureMessage,
configureScope,
createTransport,
- getActiveTransaction,
getCurrentHub,
getHubFromCarrier,
makeMain,
diff --git a/packages/svelte/.eslintrc.js b/packages/svelte/.eslintrc.js
index 0714feabf8d4..46d8d10cc538 100644
--- a/packages/svelte/.eslintrc.js
+++ b/packages/svelte/.eslintrc.js
@@ -3,7 +3,4 @@ module.exports = {
browser: true,
},
extends: ['../../.eslintrc.js'],
- rules: {
- '@sentry-internal/sdk/no-unsupported-es6-methods': 'off',
- },
};
diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json
index 2293cdcd42cb..ab2e9ee19ac7 100644
--- a/packages/sveltekit/package.json
+++ b/packages/sveltekit/package.json
@@ -30,7 +30,6 @@
},
"devDependencies": {
"@sveltejs/kit": "^1.11.0",
- "svelte": "^3.44.0",
"typescript": "^4.9.3",
"vite": "4.0.0"
},
diff --git a/packages/sveltekit/rollup.npm.config.js b/packages/sveltekit/rollup.npm.config.js
index f9dfe71fd30c..f1f8240d5a7a 100644
--- a/packages/sveltekit/rollup.npm.config.js
+++ b/packages/sveltekit/rollup.npm.config.js
@@ -1,10 +1,14 @@
import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js';
-export default makeNPMConfigVariants(
- makeBaseNPMConfig({
- entrypoints: ['src/index.server.ts', 'src/index.client.ts', 'src/client/index.ts', 'src/server/index.ts'],
- packageSpecificConfig: {
- external: ['$app/stores'],
- },
- }),
-);
+export default
+ makeNPMConfigVariants(
+ makeBaseNPMConfig({
+ entrypoints: [
+ 'src/index.server.ts',
+ 'src/index.client.ts',
+ 'src/client/index.ts',
+ 'src/server/index.ts',
+ ],
+ }),
+ )
+;
diff --git a/packages/sveltekit/src/client/load.ts b/packages/sveltekit/src/client/load.ts
index 32f310e3bd2d..fbaa5f98799f 100644
--- a/packages/sveltekit/src/client/load.ts
+++ b/packages/sveltekit/src/client/load.ts
@@ -1,7 +1,6 @@
-import { trace } from '@sentry/core';
import { captureException } from '@sentry/svelte';
-import { addExceptionMechanism, objectify } from '@sentry/utils';
-import type { Load } from '@sveltejs/kit';
+import { addExceptionMechanism, isThenable, objectify } from '@sentry/utils';
+import type { ServerLoad } from '@sveltejs/kit';
function sendErrorToSentry(e: unknown): unknown {
// In case we have a primitive, wrap it in the equivalent wrapper class (string -> String, etc.) so that we can
@@ -31,24 +30,24 @@ function sendErrorToSentry(e: unknown): unknown {
*
* @param origLoad SvelteKit user defined load function
*/
-export function wrapLoadWithSentry(origLoad: Load): Load {
+export function wrapLoadWithSentry(origLoad: ServerLoad): ServerLoad {
return new Proxy(origLoad, {
- apply: (wrappingTarget, thisArg, args: Parameters) => {
- const [event] = args;
-
- const routeId = event.route.id;
- return trace(
- {
- op: 'function.sveltekit.load',
- name: routeId ? routeId : event.url.pathname,
- status: 'ok',
- metadata: {
- source: routeId ? 'route' : 'url',
- },
- },
- () => wrappingTarget.apply(thisArg, args),
- sendErrorToSentry,
- );
+ apply: (wrappingTarget, thisArg, args: Parameters) => {
+ let maybePromiseResult;
+
+ try {
+ maybePromiseResult = wrappingTarget.apply(thisArg, args);
+ } catch (e) {
+ throw sendErrorToSentry(e);
+ }
+
+ if (isThenable(maybePromiseResult)) {
+ Promise.resolve(maybePromiseResult).then(null, e => {
+ sendErrorToSentry(e);
+ });
+ }
+
+ return maybePromiseResult;
},
});
}
diff --git a/packages/sveltekit/src/client/router.ts b/packages/sveltekit/src/client/router.ts
deleted file mode 100644
index 64125bdbdbd4..000000000000
--- a/packages/sveltekit/src/client/router.ts
+++ /dev/null
@@ -1,124 +0,0 @@
-import { getActiveTransaction } from '@sentry/core';
-import { WINDOW } from '@sentry/svelte';
-import type { Span, Transaction, TransactionContext } from '@sentry/types';
-
-import { navigating, page } from '$app/stores';
-
-const DEFAULT_TAGS = {
- 'routing.instrumentation': '@sentry/sveltekit',
-};
-
-/**
- * Automatically creates pageload and navigation transactions for the client-side SvelteKit router.
- *
- * This instrumentation makes use of SvelteKit's `page` and `navigating` stores which can be accessed
- * anywhere on the client side.
- *
- * @param startTransactionFn the function used to start (idle) transactions
- * @param startTransactionOnPageLoad controls if pageload transactions should be created (defaults to `true`)
- * @param startTransactionOnLocationChange controls if navigation transactions should be created (defauls to `true`)
- */
-export function svelteKitRoutingInstrumentation(
- startTransactionFn: (context: TransactionContext) => T | undefined,
- startTransactionOnPageLoad: boolean = true,
- startTransactionOnLocationChange: boolean = true,
-): void {
- if (startTransactionOnPageLoad) {
- instrumentPageload(startTransactionFn);
- }
-
- if (startTransactionOnLocationChange) {
- instrumentNavigations(startTransactionFn);
- }
-}
-
-function instrumentPageload(startTransactionFn: (context: TransactionContext) => Transaction | undefined): void {
- const initialPath = WINDOW && WINDOW.location && WINDOW.location.pathname;
-
- const pageloadTransaction = startTransactionFn({
- name: initialPath,
- op: 'pageload',
- description: initialPath,
- tags: {
- ...DEFAULT_TAGS,
- },
- metadata: {
- source: 'url',
- },
- });
-
- page.subscribe(page => {
- if (!page) {
- return;
- }
-
- const routeId = page.route && page.route.id;
-
- if (pageloadTransaction && routeId) {
- pageloadTransaction.setName(routeId, 'route');
- }
- });
-}
-
-/**
- * Use the `navigating` store to start a transaction on navigations.
- */
-function instrumentNavigations(startTransactionFn: (context: TransactionContext) => Transaction | undefined): void {
- let routingSpan: Span | undefined = undefined;
- let activeTransaction: Transaction | undefined;
-
- navigating.subscribe(navigation => {
- if (!navigation) {
- // `navigating` emits a 'null' value when the navigation is completed.
- // So in this case, we can finish the routing span. If the transaction was an IdleTransaction,
- // it will finish automatically and if it was user-created users also need to finish it.
- if (routingSpan) {
- routingSpan.finish();
- routingSpan = undefined;
- }
- return;
- }
-
- const from = navigation.from;
- const to = navigation.to;
-
- // for the origin we can fall back to window.location.pathname because in this emission, it still is set to the origin path
- const rawRouteOrigin = (from && from.url.pathname) || (WINDOW && WINDOW.location && WINDOW.location.pathname);
-
- const rawRouteDestination = to && to.url.pathname;
-
- // We don't want to create transactions for navigations of same origin and destination.
- // We need to look at the raw URL here because parameterized routes can still differ in their raw parameters.
- if (rawRouteOrigin === rawRouteDestination) {
- return;
- }
-
- const parameterizedRouteOrigin = from && from.route.id;
- const parameterizedRouteDestination = to && to.route.id;
-
- activeTransaction = getActiveTransaction();
-
- if (!activeTransaction) {
- activeTransaction = startTransactionFn({
- name: parameterizedRouteDestination || rawRouteDestination || 'unknown',
- op: 'navigation',
- metadata: { source: parameterizedRouteDestination ? 'route' : 'url' },
- tags: {
- ...DEFAULT_TAGS,
- },
- });
- }
-
- if (activeTransaction) {
- if (routingSpan) {
- // If a routing span is still open from a previous navigation, we finish it.
- routingSpan.finish();
- }
- routingSpan = activeTransaction.startChild({
- op: 'ui.sveltekit.routing',
- description: 'SvelteKit Route Change',
- });
- activeTransaction.setTag('from', parameterizedRouteOrigin);
- }
- });
-}
diff --git a/packages/sveltekit/src/client/sdk.ts b/packages/sveltekit/src/client/sdk.ts
index 9bf1d2cb140b..50f44bdfa353 100644
--- a/packages/sveltekit/src/client/sdk.ts
+++ b/packages/sveltekit/src/client/sdk.ts
@@ -1,18 +1,17 @@
+import { defaultRequestInstrumentationOptions } from '@sentry-internal/tracing';
import { hasTracingEnabled } from '@sentry/core';
import type { BrowserOptions } from '@sentry/svelte';
import { BrowserTracing, configureScope, init as initSvelteSdk } from '@sentry/svelte';
import { addOrUpdateIntegration } from '@sentry/utils';
import { applySdkMetadata } from '../common/metadata';
-import { svelteKitRoutingInstrumentation } from './router';
// Treeshakable guard to remove all code related to tracing
declare const __SENTRY_TRACING__: boolean;
/**
- * Initialize the client side of the Sentry SvelteKit SDK.
*
- * @param options Configuration options for the SDK.
+ * @param options
*/
export function init(options: BrowserOptions): void {
applySdkMetadata(options, ['sveltekit', 'svelte']);
@@ -34,11 +33,14 @@ function addClientIntegrations(options: BrowserOptions): void {
if (typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__) {
if (hasTracingEnabled(options)) {
const defaultBrowserTracingIntegration = new BrowserTracing({
- routingInstrumentation: svelteKitRoutingInstrumentation,
+ tracePropagationTargets: [...defaultRequestInstrumentationOptions.tracePropagationTargets],
+ // TODO: Add SvelteKit router instrumentations
+ // routingInstrumentation: sveltekitRoutingInstrumentation,
});
integrations = addOrUpdateIntegration(defaultBrowserTracingIntegration, integrations, {
- 'options.routingInstrumentation': svelteKitRoutingInstrumentation,
+ // TODO: Add SvelteKit router instrumentations
+ // options.routingInstrumentation: sveltekitRoutingInstrumentation,
});
}
}
diff --git a/packages/sveltekit/src/index.types.ts b/packages/sveltekit/src/index.types.ts
index 1c1c0576729f..5835b6863b61 100644
--- a/packages/sveltekit/src/index.types.ts
+++ b/packages/sveltekit/src/index.types.ts
@@ -9,7 +9,7 @@ export * from './server';
import type { Integration, Options, StackParser } from '@sentry/types';
// eslint-disable-next-line import/no-unresolved
-import type { HandleClientError, HandleServerError, Load, ServerLoad } from '@sveltejs/kit';
+import type { HandleClientError, HandleServerError, ServerLoad } from '@sveltejs/kit';
import type * as clientSdk from './client';
import type * as serverSdk from './server';
@@ -21,7 +21,7 @@ export declare function handleErrorWithSentry;
-export declare function wrapLoadWithSentry(origLoad: S): S;
+export declare function wrapLoadWithSentry(origLoad: S): S;
// We export a merged Integrations object so that users can (at least typing-wise) use all integrations everywhere.
export declare const Integrations: typeof clientSdk.Integrations & typeof serverSdk.Integrations;
diff --git a/packages/sveltekit/src/server/handle.ts b/packages/sveltekit/src/server/handle.ts
deleted file mode 100644
index aab69c085048..000000000000
--- a/packages/sveltekit/src/server/handle.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-/* eslint-disable @sentry-internal/sdk/no-optional-chaining */
-import type { Span } from '@sentry/core';
-import { trace } from '@sentry/core';
-import { captureException } from '@sentry/node';
-import {
- addExceptionMechanism,
- baggageHeaderToDynamicSamplingContext,
- extractTraceparentData,
- objectify,
-} from '@sentry/utils';
-import type { Handle } from '@sveltejs/kit';
-import * as domain from 'domain';
-
-function sendErrorToSentry(e: unknown): unknown {
- // In case we have a primitive, wrap it in the equivalent wrapper class (string -> String, etc.) so that we can
- // store a seen flag on it.
- const objectifiedErr = objectify(e);
-
- captureException(objectifiedErr, scope => {
- scope.addEventProcessor(event => {
- addExceptionMechanism(event, {
- type: 'sveltekit',
- handled: false,
- data: {
- function: 'handle',
- },
- });
- return event;
- });
-
- return scope;
- });
-
- return objectifiedErr;
-}
-
-/**
- * A SvelteKit handle function that wraps the request for Sentry error and
- * performance monitoring.
- *
- * Usage:
- * ```
- * // src/hooks.server.ts
- * import { sentryHandle } from '@sentry/sveltekit';
- *
- * export const handle = sentryHandle;
- *
- * // Optionally use the sequence function to add additional handlers.
- * // export const handle = sequence(sentryHandle, yourCustomHandle);
- * ```
- */
-export const sentryHandle: Handle = ({ event, resolve }) => {
- return domain.create().bind(() => {
- const sentryTraceHeader = event.request.headers.get('sentry-trace');
- const baggageHeader = event.request.headers.get('baggage');
- const traceparentData = sentryTraceHeader ? extractTraceparentData(sentryTraceHeader) : undefined;
- const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggageHeader);
-
- return trace(
- {
- op: 'http.server',
- name: `${event.request.method} ${event.route.id}`,
- status: 'ok',
- ...traceparentData,
- metadata: {
- source: 'route',
- dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
- },
- },
- async (span?: Span) => {
- const res = await resolve(event);
- if (span) {
- span.setHttpStatus(res.status);
- }
- return res;
- },
- sendErrorToSentry,
- );
- })();
-};
diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts
index 9109f29499d4..c7784d870c56 100644
--- a/packages/sveltekit/src/server/index.ts
+++ b/packages/sveltekit/src/server/index.ts
@@ -3,4 +3,3 @@ export * from '@sentry/node';
export { init } from './sdk';
export { handleErrorWithSentry } from './handleError';
export { wrapLoadWithSentry } from './load';
-export { sentryHandle } from './handle';
diff --git a/packages/sveltekit/src/server/load.ts b/packages/sveltekit/src/server/load.ts
index 6cd45704d601..ef0433091a9e 100644
--- a/packages/sveltekit/src/server/load.ts
+++ b/packages/sveltekit/src/server/load.ts
@@ -1,8 +1,6 @@
-/* eslint-disable @sentry-internal/sdk/no-optional-chaining */
import { captureException } from '@sentry/node';
import { addExceptionMechanism, isThenable, objectify } from '@sentry/utils';
-import type { HttpError, Load, ServerLoad } from '@sveltejs/kit';
-import * as domain from 'domain';
+import type { HttpError, ServerLoad } from '@sveltejs/kit';
function isHttpError(err: unknown): err is HttpError {
return typeof err === 'object' && err !== null && 'status' in err && 'body' in err;
@@ -43,27 +41,24 @@ function sendErrorToSentry(e: unknown): unknown {
*
* @param origLoad SvelteKit user defined load function
*/
-export function wrapLoadWithSentry(origLoad: T): T {
+export function wrapLoadWithSentry(origLoad: ServerLoad): ServerLoad {
return new Proxy(origLoad, {
apply: (wrappingTarget, thisArg, args: Parameters) => {
- return domain.create().bind(() => {
- let maybePromiseResult: ReturnType;
+ let maybePromiseResult;
- try {
- maybePromiseResult = wrappingTarget.apply(thisArg, args);
- } catch (e) {
- sendErrorToSentry(e);
- throw e;
- }
+ try {
+ maybePromiseResult = wrappingTarget.apply(thisArg, args);
+ } catch (e) {
+ throw sendErrorToSentry(e);
+ }
- if (isThenable(maybePromiseResult)) {
- Promise.resolve(maybePromiseResult).then(null, e => {
- sendErrorToSentry(e);
- });
- }
+ if (isThenable(maybePromiseResult)) {
+ Promise.resolve(maybePromiseResult).then(null, e => {
+ sendErrorToSentry(e);
+ });
+ }
- return maybePromiseResult;
- })();
+ return maybePromiseResult;
},
});
}
diff --git a/packages/sveltekit/test/client/load.test.ts b/packages/sveltekit/test/client/load.test.ts
index f4d18c9f9909..7cbfd3593c03 100644
--- a/packages/sveltekit/test/client/load.test.ts
+++ b/packages/sveltekit/test/client/load.test.ts
@@ -1,5 +1,5 @@
-import { addTracingExtensions, Scope } from '@sentry/svelte';
-import type { Load } from '@sveltejs/kit';
+import { Scope } from '@sentry/svelte';
+import type { ServerLoad } from '@sveltejs/kit';
import { vi } from 'vitest';
import { wrapLoadWithSentry } from '../../src/client/load';
@@ -19,19 +19,6 @@ vi.mock('@sentry/svelte', async () => {
};
});
-const mockTrace = vi.fn();
-
-vi.mock('@sentry/core', async () => {
- const original = (await vi.importActual('@sentry/core')) as any;
- return {
- ...original,
- trace: (...args: unknown[]) => {
- mockTrace(...args);
- return original.trace(...args);
- },
- };
-});
-
const mockAddExceptionMechanism = vi.fn();
vi.mock('@sentry/utils', async () => {
@@ -46,98 +33,41 @@ function getById(_id?: string) {
throw new Error('error');
}
-const MOCK_LOAD_ARGS: any = {
- params: { id: '123' },
- route: {
- id: '/users/[id]',
- },
- url: new URL('http://localhost:3000/users/123'),
- request: {
- headers: {
- get: (key: string) => {
- if (key === 'sentry-trace') {
- return '1234567890abcdef1234567890abcdef-1234567890abcdef-1';
- }
-
- if (key === 'baggage') {
- return (
- 'sentry-environment=production,sentry-release=1.0.0,sentry-transaction=dogpark,' +
- 'sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,' +
- 'sentry-trace_id=1234567890abcdef1234567890abcdef,sentry-sample_rate=1'
- );
- }
-
- return null;
- },
- },
- },
-};
-
-beforeAll(() => {
- addTracingExtensions();
-});
-
describe('wrapLoadWithSentry', () => {
beforeEach(() => {
mockCaptureException.mockClear();
mockAddExceptionMechanism.mockClear();
- mockTrace.mockClear();
mockScope = new Scope();
});
it('calls captureException', async () => {
- async function load({ params }: Parameters[0]): Promise> {
+ async function load({ params }: Parameters[0]): Promise> {
return {
post: getById(params.id),
};
}
const wrappedLoad = wrapLoadWithSentry(load);
- const res = wrappedLoad(MOCK_LOAD_ARGS);
+ const res = wrappedLoad({ params: { id: '1' } } as any);
await expect(res).rejects.toThrow();
expect(mockCaptureException).toHaveBeenCalledTimes(1);
});
- it('calls trace function', async () => {
- async function load({ params }: Parameters[0]): Promise> {
- return {
- post: params.id,
- };
- }
-
- const wrappedLoad = wrapLoadWithSentry(load);
- await wrappedLoad(MOCK_LOAD_ARGS);
-
- expect(mockTrace).toHaveBeenCalledTimes(1);
- expect(mockTrace).toHaveBeenCalledWith(
- {
- op: 'function.sveltekit.load',
- name: '/users/[id]',
- status: 'ok',
- metadata: {
- source: 'route',
- },
- },
- expect.any(Function),
- expect.any(Function),
- );
- });
-
it('adds an exception mechanism', async () => {
const addEventProcessorSpy = vi.spyOn(mockScope, 'addEventProcessor').mockImplementationOnce(callback => {
void callback({}, { event_id: 'fake-event-id' });
return mockScope;
});
- async function load({ params }: Parameters[0]): Promise> {
+ async function load({ params }: Parameters[0]): Promise> {
return {
post: getById(params.id),
};
}
const wrappedLoad = wrapLoadWithSentry(load);
- const res = wrappedLoad(MOCK_LOAD_ARGS);
+ const res = wrappedLoad({ params: { id: '1' } } as any);
await expect(res).rejects.toThrow();
expect(addEventProcessorSpy).toBeCalledTimes(1);
diff --git a/packages/sveltekit/test/client/router.test.ts b/packages/sveltekit/test/client/router.test.ts
deleted file mode 100644
index 0b95a7195176..000000000000
--- a/packages/sveltekit/test/client/router.test.ts
+++ /dev/null
@@ -1,186 +0,0 @@
-/* eslint-disable @typescript-eslint/unbound-method */
-import type { Transaction } from '@sentry/types';
-import { writable } from 'svelte/store';
-import type { SpyInstance } from 'vitest';
-import { vi } from 'vitest';
-
-import { navigating, page } from '$app/stores';
-
-import { svelteKitRoutingInstrumentation } from '../../src/client/router';
-
-// we have to overwrite the global mock from `vitest.setup.ts` here to reset the
-// `navigating` store for each test.
-vi.mock('$app/stores', async () => {
- return {
- get navigating() {
- return navigatingStore;
- },
- page: writable(),
- };
-});
-
-let navigatingStore = writable();
-
-describe('sveltekitRoutingInstrumentation', () => {
- let returnedTransaction: (Transaction & { returnedTransaction: SpyInstance }) | undefined;
- const mockedStartTransaction = vi.fn().mockImplementation(txnCtx => {
- returnedTransaction = {
- ...txnCtx,
- setName: vi.fn(),
- startChild: vi.fn().mockImplementation(ctx => {
- return { ...mockedRoutingSpan, ...ctx };
- }),
- setTag: vi.fn(),
- };
- return returnedTransaction;
- });
-
- const mockedRoutingSpan = {
- finish: () => {},
- };
-
- const routingSpanFinishSpy = vi.spyOn(mockedRoutingSpan, 'finish');
-
- beforeEach(() => {
- navigatingStore = writable();
- vi.clearAllMocks();
- });
-
- it("starts a pageload transaction when it's called with default params", () => {
- svelteKitRoutingInstrumentation(mockedStartTransaction);
-
- expect(mockedStartTransaction).toHaveBeenCalledTimes(1);
- expect(mockedStartTransaction).toHaveBeenCalledWith({
- name: '/',
- op: 'pageload',
- description: '/',
- tags: {
- 'routing.instrumentation': '@sentry/sveltekit',
- },
- metadata: {
- source: 'url',
- },
- });
-
- // We emit an update to the `page` store to simulate the SvelteKit router lifecycle
- // @ts-ignore This is fine because we testUtils/stores.ts defines `page` as a writable store
- page.set({ route: { id: 'testRoute' } });
-
- // This should update the transaction name with the parameterized route:
- expect(returnedTransaction?.setName).toHaveBeenCalledTimes(1);
- expect(returnedTransaction?.setName).toHaveBeenCalledWith('testRoute', 'route');
- });
-
- it("doesn't start a pageload transaction if `startTransactionOnPageLoad` is false", () => {
- svelteKitRoutingInstrumentation(mockedStartTransaction, false);
- expect(mockedStartTransaction).toHaveBeenCalledTimes(0);
- });
-
- it("doesn't start a navigation transaction when `startTransactionOnLocationChange` is false", () => {
- svelteKitRoutingInstrumentation(mockedStartTransaction, false, false);
-
- // We emit an update to the `navigating` store to simulate the SvelteKit navigation lifecycle
- // @ts-ignore This is fine because we testUtils/stores.ts defines `navigating` as a writable store
- navigating.set({
- from: { route: { id: '/users' }, url: { pathname: '/users' } },
- to: { route: { id: '/users/[id]' }, url: { pathname: '/users/7762' } },
- });
-
- // This should update the transaction name with the parameterized route:
- expect(mockedStartTransaction).toHaveBeenCalledTimes(0);
- });
-
- it('starts a navigation transaction when `startTransactionOnLocationChange` is true', () => {
- svelteKitRoutingInstrumentation(mockedStartTransaction, false, true);
-
- // We emit an update to the `navigating` store to simulate the SvelteKit navigation lifecycle
- // @ts-ignore This is fine because we testUtils/stores.ts defines `navigating` as a writable store
- navigating.set({
- from: { route: { id: '/users' }, url: { pathname: '/users' } },
- to: { route: { id: '/users/[id]' }, url: { pathname: '/users/7762' } },
- });
-
- // This should update the transaction name with the parameterized route:
- expect(mockedStartTransaction).toHaveBeenCalledTimes(1);
- expect(mockedStartTransaction).toHaveBeenCalledWith({
- name: '/users/[id]',
- op: 'navigation',
- metadata: {
- source: 'route',
- },
- tags: {
- 'routing.instrumentation': '@sentry/sveltekit',
- },
- });
-
- expect(returnedTransaction?.startChild).toHaveBeenCalledWith({
- op: 'ui.sveltekit.routing',
- description: 'SvelteKit Route Change',
- });
-
- expect(returnedTransaction?.setTag).toHaveBeenCalledWith('from', '/users');
-
- // We emit `null` here to simulate the end of the navigation lifecycle
- // @ts-ignore this is fine
- navigating.set(null);
-
- expect(routingSpanFinishSpy).toHaveBeenCalledTimes(1);
- });
-
- describe('handling same origin and destination navigations', () => {
- it("doesn't start a navigation transaction if the raw navigation origin and destination are equal", () => {
- svelteKitRoutingInstrumentation(mockedStartTransaction, false, true);
-
- // We emit an update to the `navigating` store to simulate the SvelteKit navigation lifecycle
- // @ts-ignore This is fine because we testUtils/stores.ts defines `navigating` as a writable store
- navigating.set({
- from: { route: { id: '/users/[id]' }, url: { pathname: '/users/7762' } },
- to: { route: { id: '/users/[id]' }, url: { pathname: '/users/7762' } },
- });
-
- expect(mockedStartTransaction).toHaveBeenCalledTimes(0);
- });
-
- it('starts a navigation transaction if the raw navigation origin and destination are not equal', () => {
- svelteKitRoutingInstrumentation(mockedStartTransaction, false, true);
-
- // @ts-ignore This is fine
- navigating.set({
- from: { route: { id: '/users/[id]' }, url: { pathname: '/users/7762' } },
- to: { route: { id: '/users/[id]' }, url: { pathname: '/users/223412' } },
- });
-
- expect(mockedStartTransaction).toHaveBeenCalledTimes(1);
- expect(mockedStartTransaction).toHaveBeenCalledWith({
- name: '/users/[id]',
- op: 'navigation',
- metadata: {
- source: 'route',
- },
- tags: {
- 'routing.instrumentation': '@sentry/sveltekit',
- },
- });
-
- expect(returnedTransaction?.startChild).toHaveBeenCalledWith({
- op: 'ui.sveltekit.routing',
- description: 'SvelteKit Route Change',
- });
-
- expect(returnedTransaction?.setTag).toHaveBeenCalledWith('from', '/users/[id]');
- });
-
- it('falls back to `window.location.pathname` to determine the raw origin', () => {
- svelteKitRoutingInstrumentation(mockedStartTransaction, false, true);
-
- // window.location.pathame is "/" in tests
-
- // @ts-ignore This is fine
- navigating.set({
- to: { route: {}, url: { pathname: '/' } },
- });
-
- expect(mockedStartTransaction).toHaveBeenCalledTimes(0);
- });
- });
-});
diff --git a/packages/sveltekit/test/client/sdk.test.ts b/packages/sveltekit/test/client/sdk.test.ts
index a8353a73df3e..8e404578883d 100644
--- a/packages/sveltekit/test/client/sdk.test.ts
+++ b/packages/sveltekit/test/client/sdk.test.ts
@@ -5,7 +5,6 @@ import { SDK_VERSION, WINDOW } from '@sentry/svelte';
import { vi } from 'vitest';
import { BrowserTracing, init } from '../../src/client';
-import { svelteKitRoutingInstrumentation } from '../../src/client/router';
const svelteInit = vi.spyOn(SentrySvelte, 'init');
@@ -88,7 +87,6 @@ describe('Sentry client SDK', () => {
// This is the closest we can get to unit-testing the `__SENTRY_TRACING__` tree-shaking guard
// IRL, the code to add the integration would most likely be removed by the bundler.
- // @ts-ignore this is fine in the test
globalThis.__SENTRY_TRACING__ = false;
init({
@@ -102,35 +100,24 @@ describe('Sentry client SDK', () => {
expect(integrationsToInit).not.toContainEqual(expect.objectContaining({ name: 'BrowserTracing' }));
expect(browserTracing).toBeUndefined();
- // @ts-ignore this is fine in the test
delete globalThis.__SENTRY_TRACING__;
});
- it('Merges a user-provided BrowserTracing integration with the automatically added one', () => {
+ // TODO: this test is only meaningful once we have a routing instrumentation which we always want to add
+ // to a user-provided BrowserTracing integration (see NextJS SDK)
+ it.skip('Merges the user-provided BrowserTracing integration with the automatically added one', () => {
init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
- integrations: [
- new BrowserTracing({ tracePropagationTargets: ['myDomain.com'], startTransactionOnLocationChange: false }),
- ],
+ integrations: [new BrowserTracing({ tracePropagationTargets: ['myDomain.com'] })],
enableTracing: true,
});
const integrationsToInit = svelteInit.mock.calls[0][0].integrations;
-
- const browserTracing = (getCurrentHub().getClient() as BrowserClient)?.getIntegrationById(
- 'BrowserTracing',
- ) as BrowserTracing;
- const options = browserTracing.options;
+ const browserTracing = (getCurrentHub().getClient() as BrowserClient)?.getIntegrationById('BrowserTracing');
expect(integrationsToInit).toContainEqual(expect.objectContaining({ name: 'BrowserTracing' }));
expect(browserTracing).toBeDefined();
-
- // This shows that the user-configured options are still here
- expect(options.tracePropagationTargets).toEqual(['myDomain.com']);
- expect(options.startTransactionOnLocationChange).toBe(false);
-
- // But we force the routing instrumentation to be ours
- expect(options.routingInstrumentation).toEqual(svelteKitRoutingInstrumentation);
+ expect((browserTracing as BrowserTracing).options.tracePropagationTargets).toEqual(['myDomain.com']);
});
});
});
diff --git a/packages/sveltekit/test/server/handle.test.ts b/packages/sveltekit/test/server/handle.test.ts
deleted file mode 100644
index cf17b56aaa90..000000000000
--- a/packages/sveltekit/test/server/handle.test.ts
+++ /dev/null
@@ -1,251 +0,0 @@
-import { addTracingExtensions, Hub, makeMain, Scope } from '@sentry/core';
-import { NodeClient } from '@sentry/node';
-import type { Transaction } from '@sentry/types';
-import type { Handle } from '@sveltejs/kit';
-import { vi } from 'vitest';
-
-import { sentryHandle } from '../../src/server/handle';
-import { getDefaultNodeClientOptions } from '../utils';
-
-const mockCaptureException = vi.fn();
-let mockScope = new Scope();
-
-vi.mock('@sentry/node', async () => {
- const original = (await vi.importActual('@sentry/node')) as any;
- return {
- ...original,
- captureException: (err: unknown, cb: (arg0: unknown) => unknown) => {
- cb(mockScope);
- mockCaptureException(err, cb);
- return original.captureException(err, cb);
- },
- };
-});
-
-const mockAddExceptionMechanism = vi.fn();
-
-vi.mock('@sentry/utils', async () => {
- const original = (await vi.importActual('@sentry/utils')) as any;
- return {
- ...original,
- addExceptionMechanism: (...args: unknown[]) => mockAddExceptionMechanism(...args),
- };
-});
-
-function mockEvent(override: Record = {}): Parameters[0]['event'] {
- const event: Parameters[0]['event'] = {
- cookies: {} as any,
- fetch: () => Promise.resolve({} as any),
- getClientAddress: () => '',
- locals: {},
- params: { id: '123' },
- platform: {},
- request: {
- method: 'GET',
- headers: {
- get: () => null,
- append: () => {},
- delete: () => {},
- forEach: () => {},
- has: () => false,
- set: () => {},
- },
- } as any,
- route: { id: '/users/[id]' },
- setHeaders: () => {},
- url: new URL('http://localhost:3000/users/123'),
- isDataRequest: false,
-
- ...override,
- };
-
- return event;
-}
-
-const mockResponse = { status: 200, headers: {}, body: '' } as any;
-
-const enum Type {
- Sync = 'sync',
- Async = 'async',
-}
-
-function resolve(type: Type, isError: boolean): Parameters[0]['resolve'] {
- if (type === Type.Sync) {
- return (..._args: unknown[]) => {
- if (isError) {
- throw new Error(type);
- }
-
- return mockResponse;
- };
- }
-
- return (..._args: unknown[]) => {
- return new Promise((resolve, reject) => {
- if (isError) {
- reject(new Error(type));
- } else {
- resolve(mockResponse);
- }
- });
- };
-}
-
-let hub: Hub;
-let client: NodeClient;
-
-describe('handleSentry', () => {
- beforeAll(() => {
- addTracingExtensions();
- });
-
- beforeEach(() => {
- mockScope = new Scope();
- const options = getDefaultNodeClientOptions({ tracesSampleRate: 1.0 });
- client = new NodeClient(options);
- hub = new Hub(client);
- makeMain(hub);
-
- mockCaptureException.mockClear();
- mockAddExceptionMechanism.mockClear();
- });
-
- describe.each([
- // isSync, isError, expectedResponse
- [Type.Sync, true, undefined],
- [Type.Sync, false, mockResponse],
- [Type.Async, true, undefined],
- [Type.Async, false, mockResponse],
- ])('%s resolve with error %s', (type, isError, mockResponse) => {
- it('should return a response', async () => {
- let response: any = undefined;
- try {
- response = await sentryHandle({ event: mockEvent(), resolve: resolve(type, isError) });
- } catch (e) {
- expect(e).toBeInstanceOf(Error);
- expect(e.message).toEqual(type);
- }
-
- expect(response).toEqual(mockResponse);
- });
-
- it('creates a transaction', async () => {
- let ref: any = undefined;
- client.on('finishTransaction', (transaction: Transaction) => {
- ref = transaction;
- });
-
- try {
- await sentryHandle({ event: mockEvent(), resolve: resolve(type, isError) });
- } catch (e) {
- //
- }
-
- expect(ref).toBeDefined();
-
- expect(ref.name).toEqual('GET /users/[id]');
- expect(ref.op).toEqual('http.server');
- expect(ref.status).toEqual(isError ? 'internal_error' : 'ok');
- expect(ref.metadata.source).toEqual('route');
-
- expect(ref.endTimestamp).toBeDefined();
- });
-
- it('creates a transaction from sentry-trace header', async () => {
- const event = mockEvent({
- request: {
- headers: {
- get: (key: string) => {
- if (key === 'sentry-trace') {
- return '1234567890abcdef1234567890abcdef-1234567890abcdef-1';
- }
-
- return null;
- },
- },
- },
- });
-
- let ref: any = undefined;
- client.on('finishTransaction', (transaction: Transaction) => {
- ref = transaction;
- });
-
- try {
- await sentryHandle({ event, resolve: resolve(type, isError) });
- } catch (e) {
- //
- }
-
- expect(ref).toBeDefined();
- expect(ref.traceId).toEqual('1234567890abcdef1234567890abcdef');
- expect(ref.parentSpanId).toEqual('1234567890abcdef');
- expect(ref.sampled).toEqual(true);
- });
-
- it('creates a transaction with dynamic sampling context from baggage header', async () => {
- const event = mockEvent({
- request: {
- headers: {
- get: (key: string) => {
- if (key === 'sentry-trace') {
- return '1234567890abcdef1234567890abcdef-1234567890abcdef-1';
- }
-
- if (key === 'baggage') {
- return (
- 'sentry-environment=production,sentry-release=1.0.0,sentry-transaction=dogpark,' +
- 'sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,' +
- 'sentry-trace_id=1234567890abcdef1234567890abcdef,sentry-sample_rate=1'
- );
- }
-
- return null;
- },
- },
- },
- });
-
- let ref: any = undefined;
- client.on('finishTransaction', (transaction: Transaction) => {
- ref = transaction;
- });
-
- try {
- await sentryHandle({ event, resolve: resolve(type, isError) });
- } catch (e) {
- //
- }
-
- expect(ref).toBeDefined();
- expect(ref.metadata.dynamicSamplingContext).toEqual({
- environment: 'production',
- release: '1.0.0',
- public_key: 'dogsarebadatkeepingsecrets',
- sample_rate: '1',
- trace_id: '1234567890abcdef1234567890abcdef',
- transaction: 'dogpark',
- user_segment: 'segmentA',
- });
- });
-
- it('send errors to Sentry', async () => {
- const addEventProcessorSpy = vi.spyOn(mockScope, 'addEventProcessor').mockImplementationOnce(callback => {
- void callback({}, { event_id: 'fake-event-id' });
- return mockScope;
- });
-
- try {
- await sentryHandle({ event: mockEvent(), resolve: resolve(type, isError) });
- } catch (e) {
- expect(mockCaptureException).toBeCalledTimes(1);
- expect(addEventProcessorSpy).toBeCalledTimes(1);
- expect(mockAddExceptionMechanism).toBeCalledTimes(1);
- expect(mockAddExceptionMechanism).toBeCalledWith(
- {},
- { handled: false, type: 'sveltekit', data: { function: 'handle' } },
- );
- }
- });
- });
-});
diff --git a/packages/sveltekit/test/server/load.test.ts b/packages/sveltekit/test/server/load.test.ts
index 9278215074c1..ec2503b945c4 100644
--- a/packages/sveltekit/test/server/load.test.ts
+++ b/packages/sveltekit/test/server/load.test.ts
@@ -1,4 +1,3 @@
-import { addTracingExtensions } from '@sentry/core';
import { Scope } from '@sentry/node';
import type { ServerLoad } from '@sveltejs/kit';
import { error } from '@sveltejs/kit';
@@ -21,19 +20,6 @@ vi.mock('@sentry/node', async () => {
};
});
-const mockTrace = vi.fn();
-
-vi.mock('@sentry/core', async () => {
- const original = (await vi.importActual('@sentry/core')) as any;
- return {
- ...original,
- trace: (...args: unknown[]) => {
- mockTrace(...args);
- return original.trace(...args);
- },
- };
-});
-
const mockAddExceptionMechanism = vi.fn();
vi.mock('@sentry/utils', async () => {
@@ -48,42 +34,10 @@ function getById(_id?: string) {
throw new Error('error');
}
-const MOCK_LOAD_ARGS: any = {
- params: { id: '123' },
- route: {
- id: '/users/[id]',
- },
- url: new URL('http://localhost:3000/users/123'),
- request: {
- headers: {
- get: (key: string) => {
- if (key === 'sentry-trace') {
- return '1234567890abcdef1234567890abcdef-1234567890abcdef-1';
- }
-
- if (key === 'baggage') {
- return (
- 'sentry-environment=production,sentry-release=1.0.0,sentry-transaction=dogpark,' +
- 'sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,' +
- 'sentry-trace_id=1234567890abcdef1234567890abcdef,sentry-sample_rate=1'
- );
- }
-
- return null;
- },
- },
- },
-};
-
-beforeAll(() => {
- addTracingExtensions();
-});
-
describe('wrapLoadWithSentry', () => {
beforeEach(() => {
mockCaptureException.mockClear();
mockAddExceptionMechanism.mockClear();
- mockTrace.mockClear();
mockScope = new Scope();
});
@@ -95,50 +49,12 @@ describe('wrapLoadWithSentry', () => {
}
const wrappedLoad = wrapLoadWithSentry(load);
- const res = wrappedLoad(MOCK_LOAD_ARGS);
+ const res = wrappedLoad({ params: { id: '1' } } as any);
await expect(res).rejects.toThrow();
expect(mockCaptureException).toHaveBeenCalledTimes(1);
});
- // TODO: enable this once we figured out how tracing the load function doesn't result in creating a new transaction
- it.skip('calls trace function', async () => {
- async function load({ params }: Parameters[0]): Promise> {
- return {
- post: params.id,
- };
- }
-
- const wrappedLoad = wrapLoadWithSentry(load);
- await wrappedLoad(MOCK_LOAD_ARGS);
-
- expect(mockTrace).toHaveBeenCalledTimes(1);
- expect(mockTrace).toHaveBeenCalledWith(
- {
- op: 'function.sveltekit.load',
- name: '/users/[id]',
- parentSampled: true,
- parentSpanId: '1234567890abcdef',
- status: 'ok',
- traceId: '1234567890abcdef1234567890abcdef',
- metadata: {
- dynamicSamplingContext: {
- environment: 'production',
- public_key: 'dogsarebadatkeepingsecrets',
- release: '1.0.0',
- sample_rate: '1',
- trace_id: '1234567890abcdef1234567890abcdef',
- transaction: 'dogpark',
- user_segment: 'segmentA',
- },
- source: 'route',
- },
- },
- expect.any(Function),
- expect.any(Function),
- );
- });
-
describe('with error() helper', () => {
it.each([
// [statusCode, timesCalled]
@@ -159,7 +75,7 @@ describe('wrapLoadWithSentry', () => {
}
const wrappedLoad = wrapLoadWithSentry(load);
- const res = wrappedLoad(MOCK_LOAD_ARGS);
+ const res = wrappedLoad({ params: { id: '1' } } as any);
await expect(res).rejects.toThrow();
expect(mockCaptureException).toHaveBeenCalledTimes(times);
@@ -179,7 +95,7 @@ describe('wrapLoadWithSentry', () => {
}
const wrappedLoad = wrapLoadWithSentry(load);
- const res = wrappedLoad(MOCK_LOAD_ARGS);
+ const res = wrappedLoad({ params: { id: '1' } } as any);
await expect(res).rejects.toThrow();
expect(addEventProcessorSpy).toBeCalledTimes(1);
diff --git a/packages/sveltekit/test/utils.ts b/packages/sveltekit/test/utils.ts
deleted file mode 100644
index 993a6bd8823d..000000000000
--- a/packages/sveltekit/test/utils.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { createTransport } from '@sentry/core';
-import type { ClientOptions } from '@sentry/types';
-import { resolvedSyncPromise } from '@sentry/utils';
-
-export function getDefaultNodeClientOptions(options: Partial = {}): ClientOptions {
- return {
- integrations: [],
- transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => resolvedSyncPromise({})),
- stackParser: () => [],
- ...options,
- };
-}
diff --git a/packages/sveltekit/test/vitest.setup.ts b/packages/sveltekit/test/vitest.setup.ts
deleted file mode 100644
index 48c9b0e33528..000000000000
--- a/packages/sveltekit/test/vitest.setup.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { writable } from 'svelte/store';
-import { vi } from 'vitest';
-
-export function setup() {
- // mock $app/stores because vitest can't resolve this import from SvelteKit.
- // Seems like $app/stores is only created at build time of a SvelteKit app.
- vi.mock('$app/stores', async () => {
- return {
- navigating: writable(),
- page: writable(),
- };
- });
-}
diff --git a/packages/sveltekit/vite.config.ts b/packages/sveltekit/vite.config.ts
index c1e4297e11ea..f479704b7591 100644
--- a/packages/sveltekit/vite.config.ts
+++ b/packages/sveltekit/vite.config.ts
@@ -1,14 +1,3 @@
-import type { UserConfig } from 'vitest';
-
import baseConfig from '../../vite/vite.config';
-export default {
- ...baseConfig,
- test: {
- // test exists, no idea why TS doesn't recognize it
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- ...(baseConfig as UserConfig & { test: any }).test,
- environment: 'jsdom',
- setupFiles: ['./test/vitest.setup.ts'],
- },
-};
+export default baseConfig;
diff --git a/packages/tracing-internal/src/index.ts b/packages/tracing-internal/src/index.ts
index c4e17c25294f..c735942fbe19 100644
--- a/packages/tracing-internal/src/index.ts
+++ b/packages/tracing-internal/src/index.ts
@@ -1,15 +1,6 @@
export * from './exports';
-export {
- Apollo,
- Express,
- GraphQL,
- Mongo,
- Mysql,
- Postgres,
- Prisma,
- lazyLoadedNodePerformanceMonitoringIntegrations,
-} from './node';
+export { Apollo, Express, GraphQL, Mongo, Mysql, Postgres, Prisma } from './node/integrations';
export {
BrowserTracing,
diff --git a/packages/tracing-internal/src/node/integrations/index.ts b/packages/tracing-internal/src/node/integrations/index.ts
index 0b69f4440f3a..607a3e129984 100644
--- a/packages/tracing-internal/src/node/integrations/index.ts
+++ b/packages/tracing-internal/src/node/integrations/index.ts
@@ -5,4 +5,3 @@ export { Mongo } from './mongo';
export { Prisma } from './prisma';
export { GraphQL } from './graphql';
export { Apollo } from './apollo';
-export * from './lazy';
diff --git a/packages/tracing-internal/src/node/integrations/lazy.ts b/packages/tracing-internal/src/node/integrations/lazy.ts
deleted file mode 100644
index f53ff756cd48..000000000000
--- a/packages/tracing-internal/src/node/integrations/lazy.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import type { Integration, IntegrationClass } from '@sentry/types';
-import { dynamicRequire } from '@sentry/utils';
-
-export const lazyLoadedNodePerformanceMonitoringIntegrations: (() => Integration)[] = [
- () => {
- const integration = dynamicRequire(module, './apollo') as {
- Apollo: IntegrationClass;
- };
- return new integration.Apollo();
- },
- () => {
- const integration = dynamicRequire(module, './apollo') as {
- Apollo: IntegrationClass;
- };
- return new integration.Apollo({ useNestjs: true });
- },
- () => {
- const integration = dynamicRequire(module, './graphql') as {
- GraphQL: IntegrationClass;
- };
- return new integration.GraphQL();
- },
- () => {
- const integration = dynamicRequire(module, './mongo') as {
- Mongo: IntegrationClass;
- };
- return new integration.Mongo();
- },
- () => {
- const integration = dynamicRequire(module, './mongo') as {
- Mongo: IntegrationClass;
- };
- return new integration.Mongo({ mongoose: true });
- },
- () => {
- const integration = dynamicRequire(module, './mysql') as {
- Mysql: IntegrationClass;
- };
- return new integration.Mysql();
- },
- () => {
- const integration = dynamicRequire(module, './postgres') as {
- Postgres: IntegrationClass;
- };
- return new integration.Postgres();
- },
-];
diff --git a/packages/tracing/package.json b/packages/tracing/package.json
index 24ec8fbb74d5..ef6d6d8f060c 100644
--- a/packages/tracing/package.json
+++ b/packages/tracing/package.json
@@ -46,7 +46,6 @@
"lint": "run-s lint:prettier lint:eslint",
"lint:eslint": "eslint . --format stylish",
"lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"",
- "validate:es5": "es-check es5 build/bundles/bundle.tracing.es5.js",
"test:unit": "jest",
"test": "jest",
"test:watch": "jest --watch",
diff --git a/packages/types/src/client.ts b/packages/types/src/client.ts
index d455c4a7590b..35b37f868d85 100644
--- a/packages/types/src/client.ts
+++ b/packages/types/src/client.ts
@@ -2,7 +2,7 @@ import type { Breadcrumb, BreadcrumbHint } from './breadcrumb';
import type { EventDropReason } from './clientreport';
import type { DataCategory } from './datacategory';
import type { DsnComponents } from './dsn';
-import type { DynamicSamplingContext, Envelope } from './envelope';
+import type { Envelope } from './envelope';
import type { Event, EventHint } from './event';
import type { Integration, IntegrationClass } from './integration';
import type { ClientOptions } from './options';
@@ -177,11 +177,6 @@ export interface Client {
*/
on?(hook: 'beforeAddBreadcrumb', callback: (breadcrumb: Breadcrumb, hint?: BreadcrumbHint) => void): void;
- /**
- * Register a callback whena DSC (Dynamic Sampling Context) is created.
- */
- on?(hook: 'createDsc', callback: (dsc: DynamicSamplingContext) => void): void;
-
/**
* Fire a hook event for transaction start and finish. Expects to be given a transaction as the
* second argument.
@@ -201,12 +196,7 @@ export interface Client {
emit?(hook: 'afterSendEvent', event: Event, sendResponse: TransportMakeRequestResponse | void): void;
/**
- * Fire a hook for when a breadcrumb is added. Expects the breadcrumb as second argument.
+ * Fire a hook for when a bredacrumb is added. Expects the breadcrumb as second argument.
*/
emit?(hook: 'beforeAddBreadcrumb', breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void;
-
- /**
- * Fire a hook for when a DSC (Dynamic Sampling Context) is created. Expects the DSC as second argument.
- */
- emit?(hook: 'createDsc', dsc: DynamicSamplingContext): void;
}
diff --git a/packages/types/src/envelope.ts b/packages/types/src/envelope.ts
index 2234317ef8ce..60d67b89d0da 100644
--- a/packages/types/src/envelope.ts
+++ b/packages/types/src/envelope.ts
@@ -18,7 +18,6 @@ export type DynamicSamplingContext = {
environment?: string;
transaction?: string;
user_segment?: string;
- replay_id?: string;
};
export type EnvelopeItemType =
diff --git a/packages/vue/src/components.ts b/packages/vue/src/components.ts
index 22ef3e530384..3de2c9baa7af 100644
--- a/packages/vue/src/components.ts
+++ b/packages/vue/src/components.ts
@@ -9,7 +9,6 @@ const ANONYMOUS_COMPONENT_NAME = '';
const repeat = (str: string, n: number): string => {
// string.repeat() is not supported by IE11, we fall back to just using the string in that case
- // eslint-disable-next-line @sentry-internal/sdk/no-unsupported-es6-methods
return str.repeat ? str.repeat(n) : str;
};
diff --git a/rollup/bundleHelpers.js b/rollup/bundleHelpers.js
index b2d25b58d248..54cd0528f271 100644
--- a/rollup/bundleHelpers.js
+++ b/rollup/bundleHelpers.js
@@ -17,7 +17,6 @@ import {
makeTerserPlugin,
makeTSPlugin,
makeSetSDKSourcePlugin,
- getEs5Polyfills,
} from './plugins/index.js';
import { mergePlugins } from './utils';
@@ -26,8 +25,6 @@ const BUNDLE_VARIANTS = ['.js', '.min.js', '.debug.min.js'];
export function makeBaseBundleConfig(options) {
const { bundleType, entrypoints, jsVersion, licenseTitle, outputFileBase, packageSpecificConfig } = options;
- const isEs5 = jsVersion.toLowerCase() === 'es5';
-
const nodeResolvePlugin = makeNodeResolvePlugin();
const sucrasePlugin = makeSucrasePlugin();
const cleanupPlugin = makeCleanupPlugin();
@@ -45,10 +42,6 @@ export function makeBaseBundleConfig(options) {
output: {
format: 'iife',
name: 'Sentry',
- outro: () => {
- // Add polyfills for ES6 array/string methods at the end of the bundle
- return isEs5 ? getEs5Polyfills() : '';
- },
},
context: 'window',
plugins: [markAsBrowserBuildPlugin],
@@ -108,9 +101,10 @@ export function makeBaseBundleConfig(options) {
strict: false,
esModule: false,
},
- plugins: isEs5
- ? [tsPlugin, nodeResolvePlugin, cleanupPlugin, licensePlugin]
- : [sucrasePlugin, nodeResolvePlugin, cleanupPlugin, licensePlugin],
+ plugins:
+ jsVersion === 'es5'
+ ? [tsPlugin, nodeResolvePlugin, cleanupPlugin, licensePlugin]
+ : [sucrasePlugin, nodeResolvePlugin, cleanupPlugin, licensePlugin],
treeshake: 'smallest',
};
diff --git a/rollup/plugins/bundlePlugins.js b/rollup/plugins/bundlePlugins.js
index 80f5123ca371..81c41d310950 100644
--- a/rollup/plugins/bundlePlugins.js
+++ b/rollup/plugins/bundlePlugins.js
@@ -8,9 +8,6 @@
* Typescript plugin docs: https://github.com/ezolenko/rollup-plugin-typescript2
*/
-import * as fs from 'fs';
-import * as path from 'path';
-
import commonjs from '@rollup/plugin-commonjs';
import deepMerge from 'deepmerge';
import license from 'rollup-plugin-license';
@@ -41,11 +38,6 @@ export function makeLicensePlugin(title) {
return plugin;
}
-export function getEs5Polyfills() {
- // Note: __dirname resolves to e.g. packages/browser or packages/tracing
- return fs.readFileSync(path.join(__dirname, '../../rollup/polyfills/es5.js'), 'utf-8');
-}
-
/**
* Create a plugin to set the value of the `__SENTRY_DEBUG__` magic string.
*
diff --git a/rollup/polyfills/es5.js b/rollup/polyfills/es5.js
deleted file mode 100644
index 54bd46e62cff..000000000000
--- a/rollup/polyfills/es5.js
+++ /dev/null
@@ -1,41 +0,0 @@
-// Sentry ES5 polyfills
-if (!('includes' in Array.prototype)) {
- Array.prototype.includes = function (searchElement) {
- return this.indexOf(searchElement) > -1;
- };
-}
-if (!('find' in Array.prototype)) {
- Array.prototype.find = function (callback) {
- for (var i = 0; i < this.length; i++) {
- if (callback(this[i])) {
- return this[i];
- }
- }
- };
-}
-if (!('findIndex' in Array.prototype)) {
- Array.prototype.findIndex = function (callback) {
- for (var i = 0; i < this.length; i++) {
- if (callback(this[i])) {
- return i;
- }
- }
- return -1;
- };
-}
-if (!('includes' in String.prototype)) {
- String.prototype.includes = function (searchElement) {
- return this.indexOf(searchElement) > -1;
- };
-}
-if (!('startsWith' in String.prototype)) {
- String.prototype.startsWith = function (searchElement) {
- return this.indexOf(searchElement) === 0;
- };
-}
-if (!('endsWith' in String.prototype)) {
- String.prototype.endsWith = function (searchElement) {
- var i = this.indexOf(searchElement);
- return i > -1 && i === this.length - searchElement.length;
- };
-}
diff --git a/yarn.lock b/yarn.lock
index 7f4e13e3108b..0243a33b9b4b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2248,15 +2248,6 @@
resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7"
integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==
-"@dabh/diagnostics@^2.0.2":
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a"
- integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==
- dependencies:
- colorspace "1.1.x"
- enabled "2.0.x"
- kuler "^2.0.0"
-
"@discoveryjs/json-ext@0.5.3":
version "0.5.3"
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d"
@@ -5107,11 +5098,6 @@
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d"
integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==
-"@types/triple-beam@^1.3.2":
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.2.tgz#38ecb64f01aa0d02b7c8f4222d7c38af6316fef8"
- integrity sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==
-
"@types/uglify-js@*":
version "3.13.1"
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.13.1.tgz#5e889e9e81e94245c75b6450600e1c5ea2878aea"
@@ -5846,11 +5832,6 @@ acorn-walk@^8.0.0, acorn-walk@^8.1.1, acorn-walk@^8.2.0:
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
-acorn@8.8.2, acorn@^8.8.1, acorn@^8.8.2:
- version "8.8.2"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
- integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
-
acorn@^6.0.5, acorn@^6.4.1:
version "6.4.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
@@ -5866,6 +5847,11 @@ acorn@^8.0.4, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.0, acorn@^8.7
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
+acorn@^8.8.1, acorn@^8.8.2:
+ version "8.8.2"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
+ integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
+
add-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa"
@@ -9271,7 +9257,7 @@ collection-visit@^1.0.0:
map-visit "^1.0.0"
object-visit "^1.0.0"
-color-convert@^1.9.0, color-convert@^1.9.1, color-convert@^1.9.3:
+color-convert@^1.9.0, color-convert@^1.9.1:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
@@ -9303,14 +9289,6 @@ color-string@^1.5.4:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
-color-string@^1.6.0:
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
- integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
- dependencies:
- color-name "^1.0.0"
- simple-swizzle "^0.2.2"
-
color-support@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
@@ -9324,14 +9302,6 @@ color@^3.0.0:
color-convert "^1.9.1"
color-string "^1.5.4"
-color@^3.1.3:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164"
- integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==
- dependencies:
- color-convert "^1.9.3"
- color-string "^1.6.0"
-
colord@^2.9.1:
version "2.9.3"
resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43"
@@ -9352,14 +9322,6 @@ colors@1.4.0, colors@^1.4.0:
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
-colorspace@1.1.x:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243"
- integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==
- dependencies:
- color "^3.1.3"
- text-hex "1.0.x"
-
columnify@1.6.0:
version "1.6.0"
resolved "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3"
@@ -9385,11 +9347,6 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
-commander@10.0.0:
- version "10.0.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.0.tgz#71797971162cd3cf65f0b9d24eb28f8d303acdf1"
- integrity sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==
-
commander@2.8.x:
version "2.8.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4"
@@ -11879,11 +11836,6 @@ emojis-list@^3.0.0:
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
-enabled@2.0.x:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2"
- integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==
-
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
@@ -12068,17 +12020,6 @@ es-abstract@^1.17.2, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-
string.prototype.trimstart "^1.0.5"
unbox-primitive "^1.0.2"
-es-check@7.1.0:
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/es-check/-/es-check-7.1.0.tgz#1015db640a7b785ff4098baf2e0791c070a25964"
- integrity sha512-t099vm9tNqNHF28Q/mRcqYxmkbkoo/Qu2ZI5/D+eFeqNUjI3jwkIyHyexXiAtstbZ1FQELi0QCuUaYCtiffi4Q==
- dependencies:
- acorn "8.8.2"
- commander "10.0.0"
- fast-glob "^3.2.12"
- supports-color "^8.1.1"
- winston "^3.8.2"
-
es-module-lexer@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.7.1.tgz#c2c8e0f46f2df06274cdaf0dd3f3b33e0a0b267d"
@@ -12964,7 +12905,7 @@ fast-glob@3.2.7:
merge2 "^1.3.0"
micromatch "^4.0.4"
-fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.4, fast-glob@^3.2.5, fast-glob@^3.2.9:
+fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.4, fast-glob@^3.2.5, fast-glob@^3.2.9:
version "3.2.12"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
@@ -13059,11 +13000,6 @@ fd-slicer@~1.1.0:
dependencies:
pend "~1.2.0"
-fecha@^4.2.0:
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd"
- integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==
-
fflate@^0.4.4:
version "0.4.8"
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae"
@@ -13405,11 +13341,6 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
-fn.name@1.x.x:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
- integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
-
follow-redirects@^1.0.0, follow-redirects@^1.14.9, follow-redirects@^1.15.0:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
@@ -17057,11 +16988,6 @@ klona@^2.0.4:
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22"
integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==
-kuler@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
- integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
-
language-subtag-registry@~0.3.2:
version "0.3.22"
resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d"
@@ -17758,18 +17684,6 @@ log4js@^6.4.1:
rfdc "^1.3.0"
streamroller "^3.0.2"
-logform@^2.3.2, logform@^2.4.0:
- version "2.5.1"
- resolved "https://registry.yarnpkg.com/logform/-/logform-2.5.1.tgz#44c77c34becd71b3a42a3970c77929e52c6ed48b"
- integrity sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==
- dependencies:
- "@colors/colors" "1.5.0"
- "@types/triple-beam" "^1.3.2"
- fecha "^4.2.0"
- ms "^2.1.1"
- safe-stable-stringify "^2.3.1"
- triple-beam "^1.3.0"
-
loglevel@^1.6.8:
version "1.8.0"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114"
@@ -19936,13 +19850,6 @@ once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
-one-time@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45"
- integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==
- dependencies:
- fn.name "1.x.x"
-
onetime@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
@@ -24762,11 +24669,6 @@ stable@^0.1.8:
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
-stack-trace@0.0.x:
- version "0.0.10"
- resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
- integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==
-
stack-utils@^2.0.3:
version "2.0.5"
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5"
@@ -25333,7 +25235,7 @@ supports-color@^7.0.0, supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
-supports-color@^8.0.0, supports-color@^8.1.1:
+supports-color@^8.0.0:
version "8.1.1"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
@@ -25368,11 +25270,6 @@ svelte@3.49.0:
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.49.0.tgz#5baee3c672306de1070c3b7888fc2204e36a4029"
integrity sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA==
-svelte@^3.44.0:
- version "3.57.0"
- resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.57.0.tgz#a3969cfe51f25f2a55e75f7b98dbd02c3af0980b"
- integrity sha512-WMXEvF+RtAaclw0t3bPDTUe19pplMlfyKDsixbHQYgCWi9+O9VN0kXU1OppzrB9gPAvz4NALuoca2LfW2bOjTQ==
-
svgo@^1.0.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
@@ -25742,11 +25639,6 @@ text-extensions@^1.0.0:
resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26"
integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==
-text-hex@1.0.x:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5"
- integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==
-
text-table@0.2.0, text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -26088,11 +25980,6 @@ trim-right@^1.0.1:
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=
-triple-beam@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
- integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
-
ts-interface-checker@^0.1.9:
version "0.1.13"
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
@@ -27666,32 +27553,6 @@ wildcard@^2.0.0:
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
-winston-transport@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa"
- integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==
- dependencies:
- logform "^2.3.2"
- readable-stream "^3.6.0"
- triple-beam "^1.3.0"
-
-winston@^3.8.2:
- version "3.8.2"
- resolved "https://registry.yarnpkg.com/winston/-/winston-3.8.2.tgz#56e16b34022eb4cff2638196d9646d7430fdad50"
- integrity sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==
- dependencies:
- "@colors/colors" "1.5.0"
- "@dabh/diagnostics" "^2.0.2"
- async "^3.2.3"
- is-stream "^2.0.0"
- logform "^2.4.0"
- one-time "^1.0.0"
- readable-stream "^3.4.0"
- safe-stable-stringify "^2.3.1"
- stack-trace "0.0.x"
- triple-beam "^1.3.0"
- winston-transport "^4.5.0"
-
word-wrap@^1.2.3, word-wrap@~1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"