From 89eab5be78f03363091e76f09a62fa17992854de Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Mon, 26 Aug 2024 13:58:59 +0200 Subject: [PATCH 01/64] meta: Update Changelog for 8.27.0 --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7864efac6871..3742f81fc83d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,25 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 8.27.0 + +### Important Changes + +- **fix(nestjs): Exception filters in main app module are not being executed (#13278)** + + With this release nestjs error monitoring is no longer automatically set up after adding the `SentryModule` to your + application, which led to issues in certain scenarios. You will now have to either add the `SentryGlobalFilter` to your + main module providers or decorate the `catch()` method in your existing global exception filters with the newly + released `@WithSentry()` decorator. See the [docs](https://docs.sentry.io/platforms/javascript/guides/nestjs/) for + more details. + +### Other Changes + +- feat: Add options for passing nonces to feedback integration (#13347) +- feat: Add support for SENTRY_SPOTLIGHT env var in Node (#13325) +- feat(deps): bump @prisma/instrumentation from 5.17.0 to 5.18.0 (#13327) +- fix(deno): Don't rely on `Deno.permissions.querySync` (#13378) + Work in this release was contributed by @charpeni. Thank you for your contribution! ## 8.26.0 From 9d7c483525e7c2f24f770eab856fef68174c6712 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Mon, 26 Aug 2024 14:05:03 +0200 Subject: [PATCH 02/64] meta: fix formatting --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3742f81fc83d..6c187aebffe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,9 +17,9 @@ - **fix(nestjs): Exception filters in main app module are not being executed (#13278)** With this release nestjs error monitoring is no longer automatically set up after adding the `SentryModule` to your - application, which led to issues in certain scenarios. You will now have to either add the `SentryGlobalFilter` to your - main module providers or decorate the `catch()` method in your existing global exception filters with the newly - released `@WithSentry()` decorator. See the [docs](https://docs.sentry.io/platforms/javascript/guides/nestjs/) for + application, which led to issues in certain scenarios. You will now have to either add the `SentryGlobalFilter` to + your main module providers or decorate the `catch()` method in your existing global exception filters with the newly + released `@WithSentry()` decorator. See the [docs](https://docs.sentry.io/platforms/javascript/guides/nestjs/) for more details. ### Other Changes From bb2d34f91644c44ffba5b8fd09b53d030ec9792c Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 26 Aug 2024 13:36:24 +0000 Subject: [PATCH 03/64] ci: Pin node 22 for Node.js unit tests --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 516b1d47b624..c66b6e64c8e8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -454,7 +454,8 @@ jobs: strategy: fail-fast: false matrix: - node: [14, 16, 18, 20, 22] + # TODO(lforst): Unpin Node.js version 22 when https://github.com/protobufjs/protobuf.js/issues/2025 is resolved which broke the nodejs tests + node: [14, 16, 18, 20, '22.6.0'] steps: - name: Check out base commit (${{ github.event.pull_request.base.sha }}) uses: actions/checkout@v4 From 205f1275aaa0c25e63472d9d24125a8b30b011e9 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Tue, 27 Aug 2024 13:27:51 +0200 Subject: [PATCH 04/64] meta: Update CHANGELOG for 8.27.0 --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7864efac6871..6fa005af46c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,27 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 8.27.0 + +### Important Changes + +- **fix(nestjs): Exception filters in main app module are not being executed (#13278)** + + With this release nestjs error monitoring is no longer automatically set up after adding the `SentryModule` to your + application, which led to issues in certain scenarios. You will now have to either add the `SentryGlobalFilter` to + your main module providers or decorate the `catch()` method in your existing global exception filters with the newly + released `@WithSentry()` decorator. See the [docs](https://docs.sentry.io/platforms/javascript/guides/nestjs/) for + more details. + +### Other Changes + +- feat: Add options for passing nonces to feedback integration (#13347) +- feat: Add support for SENTRY_SPOTLIGHT env var in Node (#13325) +- feat(deps): bump @prisma/instrumentation from 5.17.0 to 5.18.0 (#13327) +- feat(feedback): Improve error message for 403 errors (#13441) +- fix(deno): Don't rely on `Deno.permissions.querySync` (#13378) +- fix(replay): Ensure we publish replay CDN bundles (#13437) + Work in this release was contributed by @charpeni. Thank you for your contribution! ## 8.26.0 From 66a6316b28bb1dd89773f2c7a23278c54ad1b4b1 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Tue, 27 Aug 2024 12:02:40 +0000 Subject: [PATCH 05/64] release: 8.27.0 --- .../browser-integration-tests/package.json | 4 ++-- .../bundle-analyzer-scenarios/package.json | 2 +- dev-packages/e2e-tests/package.json | 2 +- .../package.json | 2 +- .../node-integration-tests/package.json | 10 +++++----- dev-packages/overhead-metrics/package.json | 2 +- dev-packages/rollup-utils/package.json | 2 +- dev-packages/size-limit-gh-action/package.json | 2 +- dev-packages/test-utils/package.json | 6 +++--- lerna.json | 2 +- packages/angular/package.json | 10 +++++----- packages/astro/package.json | 12 ++++++------ packages/aws-serverless/package.json | 10 +++++----- packages/browser-utils/package.json | 8 ++++---- packages/browser/package.json | 18 +++++++++--------- packages/bun/package.json | 12 ++++++------ packages/cloudflare/package.json | 8 ++++---- packages/core/package.json | 6 +++--- packages/deno/package.json | 8 ++++---- packages/ember/package.json | 10 +++++----- packages/eslint-config-sdk/package.json | 6 +++--- packages/eslint-plugin-sdk/package.json | 2 +- packages/feedback/package.json | 8 ++++---- packages/gatsby/package.json | 10 +++++----- packages/google-cloud-serverless/package.json | 10 +++++----- packages/integration-shims/package.json | 8 ++++---- packages/nestjs/package.json | 10 +++++----- packages/nextjs/package.json | 16 ++++++++-------- packages/node/package.json | 10 +++++----- packages/nuxt/package.json | 16 ++++++++-------- packages/opentelemetry/package.json | 8 ++++---- packages/profiling-node/package.json | 10 +++++----- packages/react/package.json | 10 +++++----- packages/remix/package.json | 14 +++++++------- packages/replay-canvas/package.json | 10 +++++----- packages/replay-internal/package.json | 12 ++++++------ packages/replay-worker/package.json | 2 +- packages/solid/package.json | 10 +++++----- packages/solidstart/package.json | 14 +++++++------- packages/svelte/package.json | 10 +++++----- packages/sveltekit/package.json | 14 +++++++------- packages/types/package.json | 2 +- packages/typescript/package.json | 2 +- packages/utils/package.json | 4 ++-- packages/utils/src/version.ts | 2 +- packages/vercel-edge/package.json | 8 ++++---- packages/vue/package.json | 10 +++++----- packages/wasm/package.json | 10 +++++----- 48 files changed, 192 insertions(+), 192 deletions(-) diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index dfd3b78c2c14..079025b3ec0a 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-integration-tests", - "version": "8.26.0", + "version": "8.27.0", "main": "index.js", "license": "MIT", "engines": { @@ -43,7 +43,7 @@ "@babel/preset-typescript": "^7.16.7", "@playwright/test": "^1.44.1", "@sentry-internal/rrweb": "2.11.0", - "@sentry/browser": "8.26.0", + "@sentry/browser": "8.27.0", "axios": "1.6.7", "babel-loader": "^8.2.2", "html-webpack-plugin": "^5.5.0", diff --git a/dev-packages/bundle-analyzer-scenarios/package.json b/dev-packages/bundle-analyzer-scenarios/package.json index a9f2c9265e72..2a785b9face2 100644 --- a/dev-packages/bundle-analyzer-scenarios/package.json +++ b/dev-packages/bundle-analyzer-scenarios/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/bundle-analyzer-scenarios", - "version": "8.26.0", + "version": "8.27.0", "description": "Scenarios to test bundle analysis with", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/dev-packages/bundle-analyzer-scenarios", diff --git a/dev-packages/e2e-tests/package.json b/dev-packages/e2e-tests/package.json index 69211cd141a8..687dac45a0c8 100644 --- a/dev-packages/e2e-tests/package.json +++ b/dev-packages/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/e2e-tests", - "version": "8.26.0", + "version": "8.27.0", "license": "MIT", "private": true, "scripts": { diff --git a/dev-packages/external-contributor-gh-action/package.json b/dev-packages/external-contributor-gh-action/package.json index 3d81148be51a..c4cdf891b886 100644 --- a/dev-packages/external-contributor-gh-action/package.json +++ b/dev-packages/external-contributor-gh-action/package.json @@ -1,7 +1,7 @@ { "name": "@sentry-internal/external-contributor-gh-action", "description": "An internal Github Action to add external contributors to the CHANGELOG.md file.", - "version": "8.26.0", + "version": "8.27.0", "license": "MIT", "engines": { "node": ">=18" diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index e381dc99e5d6..fc8577a06ca0 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/node-integration-tests", - "version": "8.26.0", + "version": "8.27.0", "license": "MIT", "engines": { "node": ">=14.18" @@ -31,10 +31,10 @@ "@nestjs/core": "^10.3.3", "@nestjs/platform-express": "^10.3.3", "@prisma/client": "5.9.1", - "@sentry/aws-serverless": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/aws-serverless": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@types/mongodb": "^3.6.20", "@types/mysql": "^2.15.21", "@types/pg": "^8.6.5", diff --git a/dev-packages/overhead-metrics/package.json b/dev-packages/overhead-metrics/package.json index 4b4825b185df..3b24ab301e7d 100644 --- a/dev-packages/overhead-metrics/package.json +++ b/dev-packages/overhead-metrics/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "8.26.0", + "version": "8.27.0", "name": "@sentry-internal/overhead-metrics", "main": "index.js", "author": "Sentry", diff --git a/dev-packages/rollup-utils/package.json b/dev-packages/rollup-utils/package.json index 6ac6f0a1a729..68f7a42bc41b 100644 --- a/dev-packages/rollup-utils/package.json +++ b/dev-packages/rollup-utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/rollup-utils", - "version": "8.26.0", + "version": "8.27.0", "description": "Rollup utilities used at Sentry for the Sentry JavaScript SDK", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/rollup-utils", diff --git a/dev-packages/size-limit-gh-action/package.json b/dev-packages/size-limit-gh-action/package.json index 7d76088b54b6..b383a4b302e7 100644 --- a/dev-packages/size-limit-gh-action/package.json +++ b/dev-packages/size-limit-gh-action/package.json @@ -1,7 +1,7 @@ { "name": "@sentry-internal/size-limit-gh-action", "description": "An internal Github Action to compare the current size of a PR against the one on develop.", - "version": "8.26.0", + "version": "8.27.0", "license": "MIT", "engines": { "node": ">=18" diff --git a/dev-packages/test-utils/package.json b/dev-packages/test-utils/package.json index a0b1688f4142..9db6d9bd38b5 100644 --- a/dev-packages/test-utils/package.json +++ b/dev-packages/test-utils/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "8.26.0", + "version": "8.27.0", "name": "@sentry-internal/test-utils", "author": "Sentry", "license": "MIT", @@ -45,8 +45,8 @@ }, "devDependencies": { "@playwright/test": "^1.44.1", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "volta": { "extends": "../../package.json" diff --git a/lerna.json b/lerna.json index e3c3f83f3096..d36af7896191 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "8.26.0", + "version": "8.27.0", "npmClient": "yarn" } diff --git a/packages/angular/package.json b/packages/angular/package.json index a6c6bb06ef94..10a61dbeaeb1 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/angular", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Angular", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/angular", @@ -21,10 +21,10 @@ "rxjs": "^6.5.5 || ^7.x" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "tslib": "^2.4.1" }, "devDependencies": { diff --git a/packages/astro/package.json b/packages/astro/package.json index b5fd94e25d3a..4b7c80993dfb 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/astro", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Astro", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/astro", @@ -56,11 +56,11 @@ "astro": ">=3.x || >=4.0.0-beta" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@sentry/vite-plugin": "^2.20.1" }, "devDependencies": { diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index 881976a8db34..418e2a64a56a 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/aws-serverless", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for AWS Lambda and AWS Serverless Environments", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/serverless", @@ -66,10 +66,10 @@ "dependencies": { "@opentelemetry/instrumentation-aws-lambda": "0.43.0", "@opentelemetry/instrumentation-aws-sdk": "0.43.1", - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@types/aws-lambda": "^8.10.62" }, "devDependencies": { diff --git a/packages/browser-utils/package.json b/packages/browser-utils/package.json index 235032606e5d..5c6a0ba6ba00 100644 --- a/packages/browser-utils/package.json +++ b/packages/browser-utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-utils", - "version": "8.26.0", + "version": "8.27.0", "description": "Browser Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser-utils", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/browser/package.json b/packages/browser/package.json index 07322e621598..736a3a0349a5 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/browser", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for browsers", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser", @@ -39,16 +39,16 @@ "access": "public" }, "dependencies": { - "@sentry-internal/browser-utils": "8.26.0", - "@sentry-internal/feedback": "8.26.0", - "@sentry-internal/replay": "8.26.0", - "@sentry-internal/replay-canvas": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry-internal/browser-utils": "8.27.0", + "@sentry-internal/feedback": "8.27.0", + "@sentry-internal/replay": "8.27.0", + "@sentry-internal/replay-canvas": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "devDependencies": { - "@sentry-internal/integration-shims": "8.26.0", + "@sentry-internal/integration-shims": "8.27.0", "fake-indexeddb": "^4.0.1" }, "scripts": { diff --git a/packages/bun/package.json b/packages/bun/package.json index fe0c181bf70a..3c730acc137f 100644 --- a/packages/bun/package.json +++ b/packages/bun/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/bun", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for bun", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/bun", @@ -39,11 +39,11 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "devDependencies": { "bun-types": "latest" diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json index fd9d9bcffe36..ed5ca89b8094 100644 --- a/packages/cloudflare/package.json +++ b/packages/cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/cloudflare", - "version": "8.26.0", + "version": "8.27.0", "description": "Offical Sentry SDK for Cloudflare Workers and Pages", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/cloudflare", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "optionalDependencies": { "@cloudflare/workers-types": "^4.x" diff --git a/packages/core/package.json b/packages/core/package.json index 8f140bfbb09c..a9bd50bc0fba 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/core", - "version": "8.26.0", + "version": "8.27.0", "description": "Base implementation for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/core", @@ -39,8 +39,8 @@ "access": "public" }, "dependencies": { - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/deno/package.json b/packages/deno/package.json index 4178c41cff65..2e45b6cab9a7 100644 --- a/packages/deno/package.json +++ b/packages/deno/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/deno", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Deno", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/deno", @@ -24,9 +24,9 @@ "/build" ], "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "devDependencies": { "@rollup/plugin-typescript": "^11.1.5", diff --git a/packages/ember/package.json b/packages/ember/package.json index 046d187d8da3..86cee9a07704 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/ember", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Ember.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/ember", @@ -33,10 +33,10 @@ "dependencies": { "@babel/core": "^7.24.4", "@embroider/macros": "^1.16.0", - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "ember-auto-import": "^2.7.2", "ember-cli-babel": "^8.2.0", "ember-cli-htmlbars": "^6.1.1", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index f7d26ab13625..9150ae217cb9 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-config-sdk", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK eslint config", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-config-sdk", @@ -22,8 +22,8 @@ "access": "public" }, "dependencies": { - "@sentry-internal/eslint-plugin-sdk": "8.26.0", - "@sentry-internal/typescript": "8.26.0", + "@sentry-internal/eslint-plugin-sdk": "8.27.0", + "@sentry-internal/typescript": "8.27.0", "@typescript-eslint/eslint-plugin": "^5.48.0", "@typescript-eslint/parser": "^5.48.0", "eslint-config-prettier": "^6.11.0", diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index 6f01f96eee3e..9d947d2c3f03 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-plugin-sdk", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK eslint plugin", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-plugin-sdk", diff --git a/packages/feedback/package.json b/packages/feedback/package.json index ff4c10fcad8a..d69f42e7d896 100644 --- a/packages/feedback/package.json +++ b/packages/feedback/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/feedback", - "version": "8.26.0", + "version": "8.27.0", "description": "Sentry SDK integration for user feedback", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/feedback", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "devDependencies": { "preact": "^10.19.4" diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 4bfd08a2dc80..b588a4daa156 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/gatsby", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Gatsby.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/gatsby", @@ -45,10 +45,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/react": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/react": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@sentry/webpack-plugin": "2.16.0" }, "peerDependencies": { diff --git a/packages/google-cloud-serverless/package.json b/packages/google-cloud-serverless/package.json index d8bcc758752b..2d5e364984cd 100644 --- a/packages/google-cloud-serverless/package.json +++ b/packages/google-cloud-serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/google-cloud-serverless", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Google Cloud Functions", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/google-cloud", @@ -48,10 +48,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@types/express": "^4.17.14" }, "devDependencies": { diff --git a/packages/integration-shims/package.json b/packages/integration-shims/package.json index 88eee4b1175a..ff8dfc0f155b 100644 --- a/packages/integration-shims/package.json +++ b/packages/integration-shims/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/integration-shims", - "version": "8.26.0", + "version": "8.27.0", "description": "Shims for integrations in Sentry SDK.", "main": "build/cjs/index.js", "module": "build/esm/index.js", @@ -55,9 +55,9 @@ "url": "https://github.com/getsentry/sentry-javascript/issues" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "engines": { "node": ">=14.18" diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index 902b89d11591..9d084af2a92f 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nestjs", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for NestJS", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nestjs", @@ -44,10 +44,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "devDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 466a98f8512f..593a23c5d133 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nextjs", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Next.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nextjs", @@ -71,13 +71,13 @@ "@opentelemetry/instrumentation-http": "0.52.1", "@opentelemetry/semantic-conventions": "^1.25.1", "@rollup/plugin-commonjs": "26.0.1", - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/react": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", - "@sentry/vercel-edge": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/react": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", + "@sentry/vercel-edge": "8.27.0", "@sentry/webpack-plugin": "2.20.1", "chalk": "3.0.0", "resolve": "1.22.8", diff --git a/packages/node/package.json b/packages/node/package.json index fa8fa1a797c7..e6a1b0a7feec 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/node", - "version": "8.26.0", + "version": "8.27.0", "description": "Sentry Node SDK using OpenTelemetry for performance instrumentation", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/node", @@ -89,10 +89,10 @@ "@opentelemetry/sdk-trace-base": "^1.25.1", "@opentelemetry/semantic-conventions": "^1.25.1", "@prisma/instrumentation": "5.18.0", - "@sentry/core": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "import-in-the-middle": "^1.11.0" }, "devDependencies": { diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 2216991b5e6e..bf6e48bbf1d7 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nuxt", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Nuxt (EXPERIMENTAL)", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nuxt", @@ -43,14 +43,14 @@ }, "dependencies": { "@nuxt/kit": "^3.12.2", - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@sentry/vite-plugin": "2.20.1", - "@sentry/vue": "8.26.0" + "@sentry/vue": "8.27.0" }, "devDependencies": { "@nuxt/module-builder": "0.8.1", diff --git a/packages/opentelemetry/package.json b/packages/opentelemetry/package.json index 1ffc241172f9..34c498aa74c5 100644 --- a/packages/opentelemetry/package.json +++ b/packages/opentelemetry/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/opentelemetry", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry utilities for OpenTelemetry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/opentelemetry", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", diff --git a/packages/profiling-node/package.json b/packages/profiling-node/package.json index a779edc4e732..42795df3d0bc 100644 --- a/packages/profiling-node/package.json +++ b/packages/profiling-node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/profiling-node", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Node.js Profiling", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/profiling-node", @@ -75,10 +75,10 @@ "test": "cross-env SENTRY_PROFILER_BINARY_DIR=lib jest --config jest.config.js" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "detect-libc": "^2.0.2", "node-abi": "^3.61.0" }, diff --git a/packages/react/package.json b/packages/react/package.json index 9f2c8d5c3c12..f5e11fe94f99 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/react", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for React.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/react", @@ -39,10 +39,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "hoist-non-react-statics": "^3.3.2" }, "peerDependencies": { diff --git a/packages/remix/package.json b/packages/remix/package.json index 120f4592d94a..a7ba4b4dae0b 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/remix", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Remix", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/remix", @@ -55,12 +55,12 @@ "@opentelemetry/instrumentation-http": "0.52.1", "@remix-run/router": "1.x", "@sentry/cli": "^2.33.0", - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/react": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/react": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "glob": "^10.3.4", "opentelemetry-instrumentation-remix": "0.7.1", "yargs": "^17.6.0" diff --git a/packages/replay-canvas/package.json b/packages/replay-canvas/package.json index 4dd27ce4a18c..5dcb50c0ec06 100644 --- a/packages/replay-canvas/package.json +++ b/packages/replay-canvas/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/replay-canvas", - "version": "8.26.0", + "version": "8.27.0", "description": "Replay canvas integration", "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", @@ -68,10 +68,10 @@ "@sentry-internal/rrweb": "2.25.0" }, "dependencies": { - "@sentry-internal/replay": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry-internal/replay": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "engines": { "node": ">=14.18" diff --git a/packages/replay-internal/package.json b/packages/replay-internal/package.json index cf655cbf1a38..41913f084abb 100644 --- a/packages/replay-internal/package.json +++ b/packages/replay-internal/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/replay", - "version": "8.26.0", + "version": "8.27.0", "description": "User replays for Sentry", "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", @@ -68,7 +68,7 @@ "homepage": "https://docs.sentry.io/platforms/javascript/session-replay/", "devDependencies": { "@babel/core": "^7.17.5", - "@sentry-internal/replay-worker": "8.26.0", + "@sentry-internal/replay-worker": "8.27.0", "@sentry-internal/rrweb": "2.25.0", "@sentry-internal/rrweb-snapshot": "2.25.0", "fflate": "^0.8.1", @@ -76,10 +76,10 @@ "jsdom-worker": "^0.2.1" }, "dependencies": { - "@sentry-internal/browser-utils": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry-internal/browser-utils": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "engines": { "node": ">=14.18" diff --git a/packages/replay-worker/package.json b/packages/replay-worker/package.json index 337490ecd4f2..d62c06034c49 100644 --- a/packages/replay-worker/package.json +++ b/packages/replay-worker/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/replay-worker", - "version": "8.26.0", + "version": "8.27.0", "description": "Worker for @sentry-internal/replay", "main": "build/esm/index.js", "module": "build/esm/index.js", diff --git a/packages/solid/package.json b/packages/solid/package.json index e8c7547014c0..be3e51a131c4 100644 --- a/packages/solid/package.json +++ b/packages/solid/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/solid", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Solid", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/solid", @@ -44,10 +44,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "peerDependencies": { "@solidjs/router": "^0.13.4", diff --git a/packages/solidstart/package.json b/packages/solidstart/package.json index 3251549a62b5..ee9fd2e4c956 100644 --- a/packages/solidstart/package.json +++ b/packages/solidstart/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/solidstart", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Solid Start", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/solidstart", @@ -78,12 +78,12 @@ }, "dependencies": { "@opentelemetry/instrumentation": "^0.52.1", - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/solid": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/solid": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@sentry/vite-plugin": "2.19.0" }, "devDependencies": { diff --git a/packages/svelte/package.json b/packages/svelte/package.json index cd01ba37e5bc..c03f0ba2ec48 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/svelte", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Svelte", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/svelte", @@ -39,10 +39,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "magic-string": "^0.30.0" }, "peerDependencies": { diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index a3df07e38eff..f332530c338c 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/sveltekit", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for SvelteKit", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/sveltekit", @@ -40,12 +40,12 @@ } }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/svelte": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/svelte": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@sentry/vite-plugin": "2.22.0", "magic-string": "0.30.7", "magicast": "0.2.8", diff --git a/packages/types/package.json b/packages/types/package.json index 3d649c4f3c0f..5b92d1766c7d 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/types", - "version": "8.26.0", + "version": "8.27.0", "description": "Types for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types", diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 890db27ce2f2..165b6db47a79 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/typescript", - "version": "8.26.0", + "version": "8.27.0", "description": "Typescript configuration used at Sentry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/typescript", diff --git a/packages/utils/package.json b/packages/utils/package.json index f9aaffaeaf56..ffd3cae5b3c5 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/utils", - "version": "8.26.0", + "version": "8.27.0", "description": "Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/utils", @@ -39,7 +39,7 @@ "access": "public" }, "dependencies": { - "@sentry/types": "8.26.0" + "@sentry/types": "8.27.0" }, "devDependencies": { "@types/array.prototype.flat": "^1.2.1", diff --git a/packages/utils/src/version.ts b/packages/utils/src/version.ts index 1b32cd60a8c4..424a201c405f 100644 --- a/packages/utils/src/version.ts +++ b/packages/utils/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = '8.26.0'; +export const SDK_VERSION = '8.27.0'; diff --git a/packages/vercel-edge/package.json b/packages/vercel-edge/package.json index 93c6b9e46201..09f9860418ae 100644 --- a/packages/vercel-edge/package.json +++ b/packages/vercel-edge/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vercel-edge", - "version": "8.26.0", + "version": "8.27.0", "description": "Offical Sentry SDK for the Vercel Edge Runtime", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vercel-edge", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "devDependencies": { "@edge-runtime/types": "3.0.1" diff --git a/packages/vue/package.json b/packages/vue/package.json index dbb639eccc40..f1604d24e948 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vue", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Vue.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vue", @@ -39,10 +39,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "peerDependencies": { "vue": "2.x || 3.x" diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 36d7116050f4..5626e5096152 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/wasm", - "version": "8.26.0", + "version": "8.27.0", "description": "Support for WASM.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/wasm", @@ -39,10 +39,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "scripts": { "build": "run-p build:transpile build:bundle build:types", From 1caf420df3227eaef93c1d263f461b4891194cec Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:16:56 +0200 Subject: [PATCH 06/64] feat(nuxt): Wrap config in nuxt context (#13457) To be able to use `useRuntimeConfig()` in the user-defined Sentry config file, it needs to be wrapped in a Nuxt context. Right now, this is only done on the client-side because the server-side is currently still in the `public` folder (this will be changed) and cannot use the virtual `#imports` import (`useRuntimeConfig` is imported from `#imports`) --- .../test-applications/nuxt-3/nuxt.config.ts | 7 ++++ .../nuxt-3/sentry.client.config.ts | 3 +- packages/nuxt/src/module.ts | 16 ++++++--- .../nuxt/src/runtime/plugins/sentry.client.ts | 36 ++++++++++--------- 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/nuxt.config.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/nuxt.config.ts index 69b31a4214ec..0fcccd560af9 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/nuxt.config.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/nuxt.config.ts @@ -4,4 +4,11 @@ export default defineNuxtConfig({ imports: { autoImport: false, }, + runtimeConfig: { + public: { + sentry: { + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }, + }, + }, }); diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/sentry.client.config.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/sentry.client.config.ts index 5253d08c90f0..5c4e0f892ca8 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/sentry.client.config.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/sentry.client.config.ts @@ -1,8 +1,9 @@ import * as Sentry from '@sentry/nuxt'; +import { useRuntimeConfig } from '#imports'; Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions - dsn: 'https://public@dsn.ingest.sentry.io/1337', + dsn: useRuntimeConfig().public.sentry.dsn, tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, }); diff --git a/packages/nuxt/src/module.ts b/packages/nuxt/src/module.ts index da7fcf778366..5d529c99330c 100644 --- a/packages/nuxt/src/module.ts +++ b/packages/nuxt/src/module.ts @@ -26,10 +26,18 @@ export default defineNuxtModule({ addPluginTemplate({ mode: 'client', filename: 'sentry-client-config.mjs', - getContents: () => - `import "${buildDirResolver.resolve(`/${clientConfigFile}`)}"\n` + - 'import { defineNuxtPlugin } from "#imports"\n' + - 'export default defineNuxtPlugin(() => {})', + + // Dynamic import of config file to wrap it within a Nuxt context (here: defineNuxtPlugin) + // Makes it possible to call useRuntimeConfig() in the user-defined sentry config file + getContents: () => ` + import { defineNuxtPlugin } from "#imports"; + + export default defineNuxtPlugin({ + name: 'sentry-client-config', + async setup() { + await import("${buildDirResolver.resolve(`/${clientConfigFile}`)}") + } + });`, }); addPlugin({ src: moduleDirResolver.resolve('./runtime/plugins/sentry.client'), mode: 'client' }); diff --git a/packages/nuxt/src/runtime/plugins/sentry.client.ts b/packages/nuxt/src/runtime/plugins/sentry.client.ts index a6bcd0115528..95dc954c4b89 100644 --- a/packages/nuxt/src/runtime/plugins/sentry.client.ts +++ b/packages/nuxt/src/runtime/plugins/sentry.client.ts @@ -29,24 +29,28 @@ interface VueRouter { // Tree-shakable guard to remove all code related to tracing declare const __SENTRY_TRACING__: boolean; -export default defineNuxtPlugin(nuxtApp => { - // This evaluates to true unless __SENTRY_TRACING__ is text-replaced with "false", in which case everything inside - // will get tree-shaken away - if (typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__) { - const sentryClient = getClient(); +export default defineNuxtPlugin({ + name: 'sentry-client-integrations', + dependsOn: ['sentry-client-config'], + async setup(nuxtApp) { + // This evaluates to true unless __SENTRY_TRACING__ is text-replaced with "false", in which case everything inside + // will get tree-shaken away + if (typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__) { + const sentryClient = getClient(); - if (sentryClient && '$router' in nuxtApp) { - sentryClient.addIntegration( - browserTracingIntegration({ router: nuxtApp.$router as VueRouter, routeLabel: 'path' }), - ); + if (sentryClient && '$router' in nuxtApp) { + sentryClient.addIntegration( + browserTracingIntegration({ router: nuxtApp.$router as VueRouter, routeLabel: 'path' }), + ); + } } - } - nuxtApp.hook('app:created', vueApp => { - const sentryClient = getClient(); + nuxtApp.hook('app:created', vueApp => { + const sentryClient = getClient(); - if (sentryClient) { - sentryClient.addIntegration(vueIntegration({ app: vueApp })); - } - }); + if (sentryClient) { + sentryClient.addIntegration(vueIntegration({ app: vueApp })); + } + }); + }, }); From 66bf03bc91f28cb072e5893ea059879c07a835c5 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 27 Aug 2024 14:30:46 +0200 Subject: [PATCH 07/64] test(ci): Pin Node 22 unit test Node version (#13476) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's a bug in Node 22.7.0 with protobuf which we're running into: https://github.com/protobufjs/protobuf.js/issues/2025 Once the bug is fixed, we should revert this PR. h/t @lforst for figuring this out; I'm just merging it into develop 😅 --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 39b024bd77dd..37c3089f1d2e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -456,7 +456,8 @@ jobs: strategy: fail-fast: false matrix: - node: [14, 16, 18, 20, 22] + #TODO: unpin 22 once Node bug is fixed + node: [14, 16, 18, 20, '22.6.0'] steps: - name: Check out base commit (${{ github.event.pull_request.base.sha }}) uses: actions/checkout@v4 From aaaaa7d94a85bffb896fc1cca1fd9ff12f43074d Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 27 Aug 2024 15:18:21 +0200 Subject: [PATCH 08/64] ci: Don't fork bomb on yarn install (#13478) whoops --- dev-packages/browser-integration-tests/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index dfd3b78c2c14..ffd35696f14d 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -9,7 +9,7 @@ "private": true, "scripts": { "clean": "rimraf -g suites/**/dist loader-suites/**/dist tmp", - "install-browsers": "[[ -z \"$SKIP_PLAYWRIGHT_BROWSER_INSTALL\" ]] && yarn install-browsers || echo 'Skipping browser installation'", + "install-browsers": "[[ -z \"$SKIP_PLAYWRIGHT_BROWSER_INSTALL\" ]] && yarn npx playwright install --with-deps || echo 'Skipping browser installation'", "lint": "eslint . --format stylish", "fix": "eslint . --format stylish --fix", "type-check": "tsc", From 8ce9f7ce9a122bfe70d3c12ebfe50bba5d237037 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Tue, 27 Aug 2024 18:26:27 -0230 Subject: [PATCH 09/64] feat(replay): Upgrade rrweb packages to 2.26.0 (#13483) * [fix(rrdom): Ignore invalid DOM attributes when diffing](https://github.com/getsentry/rrweb/pull/213) * [fix: manual snapshot in rAF loop](https://github.com/getsentry/rrweb/pull/210) (thanks @ShinyChang) * [feat: Fix blocking dynamically added iframes](https://github.com/getsentry/rrweb/pull/212) --- .size-limit.js | 2 +- packages/replay-canvas/package.json | 2 +- packages/replay-internal/package.json | 4 +-- yarn.lock | 42 +++++++++++++-------------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index 859ce741cc3d..2280b950c513 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -29,7 +29,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), gzip: true, - limit: '66 KB', + limit: '68 KB', modifyWebpackConfig: function (config) { const webpack = require('webpack'); config.plugins.push( diff --git a/packages/replay-canvas/package.json b/packages/replay-canvas/package.json index 4dd27ce4a18c..7bfeb7304d01 100644 --- a/packages/replay-canvas/package.json +++ b/packages/replay-canvas/package.json @@ -65,7 +65,7 @@ }, "homepage": "https://docs.sentry.io/platforms/javascript/session-replay/", "devDependencies": { - "@sentry-internal/rrweb": "2.25.0" + "@sentry-internal/rrweb": "2.26.0" }, "dependencies": { "@sentry-internal/replay": "8.26.0", diff --git a/packages/replay-internal/package.json b/packages/replay-internal/package.json index cf655cbf1a38..49ef6631c4d4 100644 --- a/packages/replay-internal/package.json +++ b/packages/replay-internal/package.json @@ -69,8 +69,8 @@ "devDependencies": { "@babel/core": "^7.17.5", "@sentry-internal/replay-worker": "8.26.0", - "@sentry-internal/rrweb": "2.25.0", - "@sentry-internal/rrweb-snapshot": "2.25.0", + "@sentry-internal/rrweb": "2.26.0", + "@sentry-internal/rrweb-snapshot": "2.26.0", "fflate": "^0.8.1", "jest-matcher-utils": "^29.0.0", "jsdom-worker": "^0.2.1" diff --git a/yarn.lock b/yarn.lock index 481175f8db61..a1a060119a64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8115,22 +8115,22 @@ dependencies: "@sentry-internal/rrweb-snapshot" "2.11.0" -"@sentry-internal/rrdom@2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.25.0.tgz#4be842f7f4efae383bbd5a9dcbbecc212d378d70" - integrity sha512-YTxGHnCdv6D2JVJ6YFezMsGOHLy7CM8x8qMaY3Yh3QTubFOjdGpcGJGITF/9Lkx+rFVCTdjL32cQu9NUgEJO8g== +"@sentry-internal/rrdom@2.26.0": + version "2.26.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.26.0.tgz#be3e4f14de56a6022aed3a00ac6ea2f2abe05d1c" + integrity sha512-QviUvwAPYDCmkeJsu3fx0pXlLBHwQLCKje9wuuhRVkmDL9dMbcCDa7+HhFa2V2UMXgZ7l6z/SMin2ymDReubSw== dependencies: - "@sentry-internal/rrweb-snapshot" "2.25.0" + "@sentry-internal/rrweb-snapshot" "2.26.0" "@sentry-internal/rrweb-snapshot@2.11.0": version "2.11.0" resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.11.0.tgz#1af79130604afea989d325465b209ac015b27c9a" integrity sha512-1nP22QlplMNooSNvTh+L30NSZ+E3UcfaJyxXSMLxUjQHTGPyM1VkndxZMmxlKhyR5X+rLbxi/+RvuAcpM43VoA== -"@sentry-internal/rrweb-snapshot@2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.25.0.tgz#f20bd20436edac24ed1075b47fc4773894739d97" - integrity sha512-7j90eSGFRS1YWcuo0bXPtV9oDdCQxutilyYbim/I09GA7kx4/d8OG8ryxQl6WWXW+E50x6dEpDsZXWMPkSleEg== +"@sentry-internal/rrweb-snapshot@2.26.0": + version "2.26.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.26.0.tgz#cb70bf2c006bf59824806f567ae6ca23ba2aac3a" + integrity sha512-wWa+OxAHhoozIlt3kjhmfrsn/+POnJgktOe5WT95fakfyv56mGKXqh4mXx7HRzGEwq4bbkhtcPhfh2gbueSPcA== "@sentry-internal/rrweb-types@2.11.0": version "2.11.0" @@ -8139,12 +8139,12 @@ dependencies: "@sentry-internal/rrweb-snapshot" "2.11.0" -"@sentry-internal/rrweb-types@2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.25.0.tgz#61662befc57ed7054a491eb35ad3deda7d66157c" - integrity sha512-sM2YdevhIRxQ/Kr89cfbNBO7/EFhycTmQT0NKg4owdKkIvuuqz1AhbRpMMdpJ4NJnos+h06VPObeXm6rcrffsw== +"@sentry-internal/rrweb-types@2.26.0": + version "2.26.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.26.0.tgz#0ba52a4e6f24238556134280b7cf77633bc68e21" + integrity sha512-og4X+OidRRc3bMuwfeio4UF8EcVFjtz/z0DDjpyV+0sH4LDoVoH1+Jlxbxl4WR83LALWMcsxV0UWYeXA5kfrOw== dependencies: - "@sentry-internal/rrweb-snapshot" "2.25.0" + "@sentry-internal/rrweb-snapshot" "2.26.0" "@types/css-font-loading-module" "0.0.7" "@sentry-internal/rrweb@2.11.0": @@ -8161,14 +8161,14 @@ fflate "^0.4.4" mitt "^3.0.0" -"@sentry-internal/rrweb@2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.25.0.tgz#0148f1904f1e9549f2c2cae209fe3d3fe891d3ec" - integrity sha512-0tgBI0CFpyO3Z3dw4IjS/D6AnQypro4dquRrcZZzqnMH65Vxw3yytGDtmvE/FzHzGC0vmKFTM+sTkzFY0bo+Bg== +"@sentry-internal/rrweb@2.26.0": + version "2.26.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.26.0.tgz#456a9ae1b48e87f086e4749346532879e9ac304c" + integrity sha512-J5db750QNlGdLrzbZwEVJgOtLwHtvh3a6VVxQ08G0yEZxqI7bdvcvxnvIXp8h+PwUk/S8yjoZwYgLFFDED3ePQ== dependencies: - "@sentry-internal/rrdom" "2.25.0" - "@sentry-internal/rrweb-snapshot" "2.25.0" - "@sentry-internal/rrweb-types" "2.25.0" + "@sentry-internal/rrdom" "2.26.0" + "@sentry-internal/rrweb-snapshot" "2.26.0" + "@sentry-internal/rrweb-types" "2.26.0" "@types/css-font-loading-module" "0.0.7" "@xstate/fsm" "^1.4.0" base64-arraybuffer "^1.0.1" From 60271f50ae1e687bba0f58656e5dc2cd0777dd9f Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 28 Aug 2024 08:29:41 +0200 Subject: [PATCH 10/64] fix(cdn): Do not mangle _metadata (#13426) Noticed that this is mangled on the `options` in the CDN, which it probably shouldn't be... --- dev-packages/rollup-utils/plugins/bundlePlugins.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs index ffc24f58174a..2ce35d1e6168 100644 --- a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs +++ b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs @@ -116,6 +116,8 @@ export function makeTerserPlugin() { '_integrations', // _meta is used to store metadata of replay network events '_meta', + // We store SDK metadata in the options + '_metadata', // Object we inject debug IDs into with bundler plugins '_sentryDebugIds', // These are used by instrument.ts in utils for identifying HTML elements & events From 3a1417fb4e244d3c4782059e67dd6235eaf13e2a Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 28 Aug 2024 09:00:27 +0200 Subject: [PATCH 11/64] ci: Update clear cache action to be smarter (#13405) Previously, we had a CI action to manually clear all caches. This PR adjusts this so this action can be used in a more granular way: * By default, the action will now delete caches of any PR runs that are successful, as well as any caches of release branches. * You can configure to also delete caches on the develop branch, and/or to also delete non-successful PR branches. Additionally, this action will run every midnight, to automatically clear completed/outdated stuff. The goal is to keep develop caches as long as possible, and clear out other caches, unless they failed (which indicates you may want to re-run some of the tests) and unless they are currently running (to not break ongoing tests). Ideally, we do not need to manually run this, but can rely on automated cleanup over night. --- .github/workflows/clear-cache.yml | 36 +++- .github/workflows/external-contributors.yml | 1 - .../clear-cache-gh-action/.eslintrc.cjs | 14 ++ dev-packages/clear-cache-gh-action/action.yml | 25 +++ dev-packages/clear-cache-gh-action/index.mjs | 183 ++++++++++++++++++ .../clear-cache-gh-action/package.json | 23 +++ package.json | 1 + 7 files changed, 280 insertions(+), 3 deletions(-) create mode 100644 dev-packages/clear-cache-gh-action/.eslintrc.cjs create mode 100644 dev-packages/clear-cache-gh-action/action.yml create mode 100644 dev-packages/clear-cache-gh-action/index.mjs create mode 100644 dev-packages/clear-cache-gh-action/package.json diff --git a/.github/workflows/clear-cache.yml b/.github/workflows/clear-cache.yml index 2946723fe6b8..5c327553e3b8 100644 --- a/.github/workflows/clear-cache.yml +++ b/.github/workflows/clear-cache.yml @@ -1,11 +1,43 @@ name: "Action: Clear all GHA caches" on: workflow_dispatch: + inputs: + clear_pending_prs: + description: Delete caches of pending PR workflows + type: boolean + default: false + clear_develop: + description: Delete caches on develop branch + type: boolean + default: false + clear_branches: + description: Delete caches on non-develop branches + type: boolean + default: true + schedule: + # Run every day at midnight + - cron: '0 0 * * *' jobs: clear-caches: name: Delete all caches runs-on: ubuntu-20.04 steps: - - name: Clear caches - uses: easimon/wipe-cache@v2 + - uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + + # TODO: Use cached version if possible (but never store cache) + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Delete GHA caches + uses: ./dev-packages/clear-cache-gh-action + with: + clear_pending_prs: ${{ inputs.clear_pending_prs }} + clear_develop: ${{ inputs.clear_develop }} + clear_branches: ${{ inputs.clear_branches }} + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/external-contributors.yml b/.github/workflows/external-contributors.yml index 50acb2be8e73..e01a1a66a589 100644 --- a/.github/workflows/external-contributors.yml +++ b/.github/workflows/external-contributors.yml @@ -25,7 +25,6 @@ jobs: uses: actions/setup-node@v4 with: node-version-file: 'package.json' - cache: 'yarn' - name: Install dependencies run: yarn install --frozen-lockfile diff --git a/dev-packages/clear-cache-gh-action/.eslintrc.cjs b/dev-packages/clear-cache-gh-action/.eslintrc.cjs new file mode 100644 index 000000000000..8c67e0037908 --- /dev/null +++ b/dev-packages/clear-cache-gh-action/.eslintrc.cjs @@ -0,0 +1,14 @@ +module.exports = { + extends: ['../../.eslintrc.js'], + parserOptions: { + sourceType: 'module', + ecmaVersion: 'latest', + }, + + overrides: [ + { + files: ['*.mjs'], + extends: ['@sentry-internal/sdk/src/base'], + }, + ], +}; diff --git a/dev-packages/clear-cache-gh-action/action.yml b/dev-packages/clear-cache-gh-action/action.yml new file mode 100644 index 000000000000..06493534b23e --- /dev/null +++ b/dev-packages/clear-cache-gh-action/action.yml @@ -0,0 +1,25 @@ +name: 'clear-cache-gh-action' +description: 'Clear caches of the GitHub repository.' +inputs: + github_token: + required: true + description: 'a github access token' + clear_develop: + required: false + default: "" + description: "If set, also clear caches from develop branch." + clear_branches: + required: false + default: "" + description: "If set, also clear caches from non-develop branches." + clear_pending_prs: + required: false + default: "" + description: "If set, also clear caches from pending PR workflow runs." + workflow_name: + required: false + default: "CI: Build & Test" + description: The workflow to clear caches for. +runs: + using: 'node20' + main: 'index.mjs' diff --git a/dev-packages/clear-cache-gh-action/index.mjs b/dev-packages/clear-cache-gh-action/index.mjs new file mode 100644 index 000000000000..b1cb75c5a5c0 --- /dev/null +++ b/dev-packages/clear-cache-gh-action/index.mjs @@ -0,0 +1,183 @@ +import * as core from '@actions/core'; + +import { context, getOctokit } from '@actions/github'; + +async function run() { + const { getInput } = core; + + const { repo, owner } = context.repo; + + const githubToken = getInput('github_token'); + const clearDevelop = inputToBoolean(getInput('clear_develop', { type: 'boolean' })); + const clearBranches = inputToBoolean(getInput('clear_branches', { type: 'boolean', default: true })); + const clearPending = inputToBoolean(getInput('clear_pending_prs', { type: 'boolean' })); + const workflowName = getInput('workflow_name'); + + const octokit = getOctokit(githubToken); + + await clearGithubCaches(octokit, { + repo, + owner, + clearDevelop, + clearPending, + clearBranches, + workflowName, + }); +} + +/** + * Clear caches. + * + * @param {ReturnType } octokit + * @param {{repo: string, owner: string, clearDevelop: boolean, clearPending: boolean, clearBranches: boolean, workflowName: string}} options + */ +async function clearGithubCaches(octokit, { repo, owner, clearDevelop, clearPending, clearBranches, workflowName }) { + let deletedCaches = 0; + let remainingCaches = 0; + + let deletedSize = 0; + let remainingSize = 0; + + /** @type {Map>} */ + const cachedPrs = new Map(); + /** @type {Map>} */ + const cachedWorkflows = new Map(); + + /** + * Clear caches. + * + * @param {{ref: string}} options + */ + const shouldClearCache = async ({ ref }) => { + // Do not clear develop caches if clearDevelop is false. + if (!clearDevelop && ref === 'refs/heads/develop') { + core.info('> Keeping cache because it is on develop.'); + return false; + } + + // There are two fundamental paths here: + // If the cache belongs to a PR, we need to check if the PR has any pending workflows. + // Else, we assume the cache belongs to a branch, where we do not check for pending workflows + const pullNumber = /^refs\/pull\/(\d+)\/merge$/.exec(ref)?.[1]; + const isPr = !!pullNumber; + + // Case 1: This is a PR, and we do not want to clear pending PRs + // In this case, we need to fetch all PRs and workflow runs to check them + if (isPr && !clearPending) { + const pr = + cachedPrs.get(pullNumber) || + (await octokit.rest.pulls.get({ + owner, + repo, + pull_number: pullNumber, + })); + cachedPrs.set(pullNumber, pr); + + const prBranch = pr.data.head.ref; + + // Check if PR has any pending workflows + const workflowRuns = + cachedWorkflows.get(prBranch) || + (await octokit.rest.actions.listWorkflowRunsForRepo({ + repo, + owner, + branch: prBranch, + })); + cachedWorkflows.set(prBranch, workflowRuns); + + // We only care about the relevant workflow + const relevantWorkflowRuns = workflowRuns.data.workflow_runs.filter(workflow => workflow.name === workflowName); + + const latestWorkflowRun = relevantWorkflowRuns[0]; + + core.info(`> Latest relevant workflow run: ${latestWorkflowRun.html_url}`); + + // No relevant workflow? Clear caches! + if (!latestWorkflowRun) { + core.info('> Clearing cache because no relevant workflow was found.'); + return true; + } + + // If the latest run was not successful, keep caches + // as either the run may be in progress, + // or failed - in which case we may want to re-run the workflow + if (latestWorkflowRun.conclusion !== 'success') { + core.info(`> Keeping cache because latest workflow is ${latestWorkflowRun.conclusion}.`); + return false; + } + + core.info(`> Clearing cache because latest workflow run is ${latestWorkflowRun.conclusion}.`); + return true; + } + + // Case 2: This is a PR, but we do want to clear pending PRs + // In this case, this cache should always be cleared + if (isPr) { + core.info('> Clearing cache of every PR workflow run.'); + return true; + } + + // Case 3: This is not a PR, and we want to clean branches + if (clearBranches) { + core.info('> Clearing cache because it is not a PR.'); + return true; + } + + // Case 4: This is not a PR, and we do not want to clean branches + core.info('> Keeping cache for non-PR workflow run.'); + return false; + }; + + for await (const response of octokit.paginate.iterator(octokit.rest.actions.getActionsCacheList, { + owner, + repo, + })) { + if (!response.data.length) { + break; + } + + for (const { id, ref, size_in_bytes } of response.data) { + core.info(`Checking cache ${id} for ${ref}...`); + + const shouldDelete = await shouldClearCache({ ref }); + + if (shouldDelete) { + core.info(`> Clearing cache ${id}...`); + + deletedCaches++; + deletedSize += size_in_bytes; + + await octokit.rest.actions.deleteActionsCacheById({ + owner, + repo, + cache_id: id, + }); + } else { + remainingCaches++; + remainingSize += size_in_bytes; + } + } + } + + const format = new Intl.NumberFormat('en-US', { + style: 'decimal', + }); + + core.info('Summary:'); + core.info(`Deleted ${deletedCaches} caches, freeing up ~${format.format(deletedSize / 1000 / 1000)} mb.`); + core.info(`Remaining ${remainingCaches} caches, using ~${format.format(remainingSize / 1000 / 1000)} mb.`); +} + +run(); + +function inputToBoolean(input) { + if (typeof input === 'boolean') { + return input; + } + + if (typeof input === 'string') { + return input === 'true'; + } + + return false; +} diff --git a/dev-packages/clear-cache-gh-action/package.json b/dev-packages/clear-cache-gh-action/package.json new file mode 100644 index 000000000000..492f4fc2b31e --- /dev/null +++ b/dev-packages/clear-cache-gh-action/package.json @@ -0,0 +1,23 @@ +{ + "name": "@sentry-internal/clear-cache-gh-action", + "description": "An internal Github Action to clear GitHub caches.", + "version": "8.26.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "private": true, + "main": "index.mjs", + "type": "module", + "scripts": { + "lint": "eslint . --format stylish", + "fix": "eslint . --format stylish --fix" + }, + "dependencies": { + "@actions/core": "1.10.1", + "@actions/github": "^5.0.0" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/package.json b/package.json index beba7d79d284..4b9ad0383c02 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "dev-packages/overhead-metrics", "dev-packages/test-utils", "dev-packages/size-limit-gh-action", + "dev-packages/clear-cache-gh-action", "dev-packages/external-contributor-gh-action", "dev-packages/rollup-utils" ], From 4ccf251f56b3f41a9d538b8b32c79c99710d9b19 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 28 Aug 2024 09:51:23 +0200 Subject: [PATCH 12/64] ci: Store build output as artifacts instead of cache (#13487) This PR changes it so that the build output is kept as an artifact, not a cache. This way, this should never be lost on us. We keep the NX cache as before. I chose a retention period of 7 days, which means that after 7 days you could no longer re-run a workflow partially. IMHO that's a reasonable start, we can adjust this if needed. --- .github/actions/restore-cache/action.yml | 12 +++++------- .github/workflows/build.yml | 17 +++++++++-------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/actions/restore-cache/action.yml b/.github/actions/restore-cache/action.yml index 848983376840..316d89dc1c26 100644 --- a/.github/actions/restore-cache/action.yml +++ b/.github/actions/restore-cache/action.yml @@ -11,15 +11,13 @@ runs: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ env.DEPENDENCY_CACHE_KEY }} - - name: Check build cache - uses: actions/cache/restore@v4 - id: build-cache + - name: Restore build artifacts + uses: actions/download-artifact@v4 with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} + name: build-output - name: Check if caches are restored uses: actions/github-script@v6 - if: steps.dep-cache.outputs.cache-hit != 'true' || steps.build-cache.outputs.cache-hit != 'true' + if: steps.dep-cache.outputs.cache-hit != 'true' with: - script: core.setFailed('Dependency or build cache could not be restored - please re-run ALL jobs.') + script: core.setFailed('Dependency cache could not be restored - please re-run ALL jobs.') diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 37c3089f1d2e..d0ae1a39c1d2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,7 +48,6 @@ env: ${{ github.workspace }}/packages/utils/cjs ${{ github.workspace }}/packages/utils/esm - BUILD_CACHE_KEY: build-cache-${{ github.event.inputs.commit || github.sha }} BUILD_CACHE_TARBALL_KEY: tarball-${{ github.event.inputs.commit || github.sha }} # GH will use the first restore-key it finds that matches @@ -160,13 +159,6 @@ jobs: base: ${{ github.event.pull_request.base.sha }} head: ${{ env.HEAD_COMMIT }} - - name: Check build cache - uses: actions/cache@v4 - id: cache_built_packages - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} - - name: NX cache uses: actions/cache@v4 # Disable cache when: @@ -188,6 +180,15 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} run: yarn build + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: build-output + path: ${{ env.CACHED_BUILD_PATHS }} + retention-days: 7 + compression-level: 6 + overwrite: true + outputs: dependency_cache_key: ${{ steps.install_dependencies.outputs.cache_key }} changed_node_integration: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry-internal/node-integration-tests') }} From eb1c10861c712ea8aef8c93d2c21f7b83c02c177 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 28 Aug 2024 10:43:05 +0200 Subject: [PATCH 13/64] chore: Manually sync master->develop (#13488) --------- Co-authored-by: nicohrubec Co-authored-by: Luca Forstner Co-authored-by: getsentry-bot Co-authored-by: getsentry-bot --- .github/workflows/build.yml | 2 +- CHANGELOG.md | 21 +++++++++++++++++++ .../browser-integration-tests/package.json | 4 ++-- .../bundle-analyzer-scenarios/package.json | 2 +- dev-packages/e2e-tests/package.json | 2 +- .../package.json | 2 +- .../node-integration-tests/package.json | 10 ++++----- dev-packages/overhead-metrics/package.json | 2 +- dev-packages/rollup-utils/package.json | 2 +- .../size-limit-gh-action/package.json | 2 +- dev-packages/test-utils/package.json | 6 +++--- lerna.json | 2 +- packages/angular/package.json | 10 ++++----- packages/astro/package.json | 12 +++++------ packages/aws-serverless/package.json | 10 ++++----- packages/browser-utils/package.json | 8 +++---- packages/browser/package.json | 18 ++++++++-------- packages/bun/package.json | 12 +++++------ packages/cloudflare/package.json | 8 +++---- packages/core/package.json | 6 +++--- packages/deno/package.json | 8 +++---- packages/ember/package.json | 10 ++++----- packages/eslint-config-sdk/package.json | 6 +++--- packages/eslint-plugin-sdk/package.json | 2 +- packages/feedback/package.json | 8 +++---- packages/gatsby/package.json | 10 ++++----- packages/google-cloud-serverless/package.json | 10 ++++----- packages/integration-shims/package.json | 8 +++---- packages/nestjs/package.json | 10 ++++----- packages/nextjs/package.json | 16 +++++++------- packages/node/package.json | 10 ++++----- packages/nuxt/package.json | 16 +++++++------- packages/opentelemetry/package.json | 8 +++---- packages/profiling-node/package.json | 10 ++++----- packages/react/package.json | 10 ++++----- packages/remix/package.json | 14 ++++++------- packages/replay-canvas/package.json | 10 ++++----- packages/replay-internal/package.json | 12 +++++------ packages/replay-worker/package.json | 2 +- packages/solid/package.json | 10 ++++----- packages/solidstart/package.json | 14 ++++++------- packages/svelte/package.json | 10 ++++----- packages/sveltekit/package.json | 14 ++++++------- packages/types/package.json | 2 +- packages/typescript/package.json | 2 +- packages/utils/package.json | 4 ++-- packages/utils/src/version.ts | 2 +- packages/vercel-edge/package.json | 8 +++---- packages/vue/package.json | 10 ++++----- packages/wasm/package.json | 10 ++++----- 50 files changed, 214 insertions(+), 193 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d0ae1a39c1d2..9ea899390b33 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -457,7 +457,7 @@ jobs: strategy: fail-fast: false matrix: - #TODO: unpin 22 once Node bug is fixed + # TODO(lforst): Unpin Node.js version 22 when https://github.com/protobufjs/protobuf.js/issues/2025 is resolved which broke the nodejs tests node: [14, 16, 18, 20, '22.6.0'] steps: - name: Check out base commit (${{ github.event.pull_request.base.sha }}) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7864efac6871..6fa005af46c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,27 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 8.27.0 + +### Important Changes + +- **fix(nestjs): Exception filters in main app module are not being executed (#13278)** + + With this release nestjs error monitoring is no longer automatically set up after adding the `SentryModule` to your + application, which led to issues in certain scenarios. You will now have to either add the `SentryGlobalFilter` to + your main module providers or decorate the `catch()` method in your existing global exception filters with the newly + released `@WithSentry()` decorator. See the [docs](https://docs.sentry.io/platforms/javascript/guides/nestjs/) for + more details. + +### Other Changes + +- feat: Add options for passing nonces to feedback integration (#13347) +- feat: Add support for SENTRY_SPOTLIGHT env var in Node (#13325) +- feat(deps): bump @prisma/instrumentation from 5.17.0 to 5.18.0 (#13327) +- feat(feedback): Improve error message for 403 errors (#13441) +- fix(deno): Don't rely on `Deno.permissions.querySync` (#13378) +- fix(replay): Ensure we publish replay CDN bundles (#13437) + Work in this release was contributed by @charpeni. Thank you for your contribution! ## 8.26.0 diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index ffd35696f14d..8c0ca22e0358 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-integration-tests", - "version": "8.26.0", + "version": "8.27.0", "main": "index.js", "license": "MIT", "engines": { @@ -43,7 +43,7 @@ "@babel/preset-typescript": "^7.16.7", "@playwright/test": "^1.44.1", "@sentry-internal/rrweb": "2.11.0", - "@sentry/browser": "8.26.0", + "@sentry/browser": "8.27.0", "axios": "1.6.7", "babel-loader": "^8.2.2", "html-webpack-plugin": "^5.5.0", diff --git a/dev-packages/bundle-analyzer-scenarios/package.json b/dev-packages/bundle-analyzer-scenarios/package.json index a9f2c9265e72..2a785b9face2 100644 --- a/dev-packages/bundle-analyzer-scenarios/package.json +++ b/dev-packages/bundle-analyzer-scenarios/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/bundle-analyzer-scenarios", - "version": "8.26.0", + "version": "8.27.0", "description": "Scenarios to test bundle analysis with", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/dev-packages/bundle-analyzer-scenarios", diff --git a/dev-packages/e2e-tests/package.json b/dev-packages/e2e-tests/package.json index 69211cd141a8..687dac45a0c8 100644 --- a/dev-packages/e2e-tests/package.json +++ b/dev-packages/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/e2e-tests", - "version": "8.26.0", + "version": "8.27.0", "license": "MIT", "private": true, "scripts": { diff --git a/dev-packages/external-contributor-gh-action/package.json b/dev-packages/external-contributor-gh-action/package.json index 3d81148be51a..c4cdf891b886 100644 --- a/dev-packages/external-contributor-gh-action/package.json +++ b/dev-packages/external-contributor-gh-action/package.json @@ -1,7 +1,7 @@ { "name": "@sentry-internal/external-contributor-gh-action", "description": "An internal Github Action to add external contributors to the CHANGELOG.md file.", - "version": "8.26.0", + "version": "8.27.0", "license": "MIT", "engines": { "node": ">=18" diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index e381dc99e5d6..fc8577a06ca0 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/node-integration-tests", - "version": "8.26.0", + "version": "8.27.0", "license": "MIT", "engines": { "node": ">=14.18" @@ -31,10 +31,10 @@ "@nestjs/core": "^10.3.3", "@nestjs/platform-express": "^10.3.3", "@prisma/client": "5.9.1", - "@sentry/aws-serverless": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/aws-serverless": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@types/mongodb": "^3.6.20", "@types/mysql": "^2.15.21", "@types/pg": "^8.6.5", diff --git a/dev-packages/overhead-metrics/package.json b/dev-packages/overhead-metrics/package.json index 4b4825b185df..3b24ab301e7d 100644 --- a/dev-packages/overhead-metrics/package.json +++ b/dev-packages/overhead-metrics/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "8.26.0", + "version": "8.27.0", "name": "@sentry-internal/overhead-metrics", "main": "index.js", "author": "Sentry", diff --git a/dev-packages/rollup-utils/package.json b/dev-packages/rollup-utils/package.json index 6ac6f0a1a729..68f7a42bc41b 100644 --- a/dev-packages/rollup-utils/package.json +++ b/dev-packages/rollup-utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/rollup-utils", - "version": "8.26.0", + "version": "8.27.0", "description": "Rollup utilities used at Sentry for the Sentry JavaScript SDK", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/rollup-utils", diff --git a/dev-packages/size-limit-gh-action/package.json b/dev-packages/size-limit-gh-action/package.json index 7d76088b54b6..b383a4b302e7 100644 --- a/dev-packages/size-limit-gh-action/package.json +++ b/dev-packages/size-limit-gh-action/package.json @@ -1,7 +1,7 @@ { "name": "@sentry-internal/size-limit-gh-action", "description": "An internal Github Action to compare the current size of a PR against the one on develop.", - "version": "8.26.0", + "version": "8.27.0", "license": "MIT", "engines": { "node": ">=18" diff --git a/dev-packages/test-utils/package.json b/dev-packages/test-utils/package.json index a0b1688f4142..9db6d9bd38b5 100644 --- a/dev-packages/test-utils/package.json +++ b/dev-packages/test-utils/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "8.26.0", + "version": "8.27.0", "name": "@sentry-internal/test-utils", "author": "Sentry", "license": "MIT", @@ -45,8 +45,8 @@ }, "devDependencies": { "@playwright/test": "^1.44.1", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "volta": { "extends": "../../package.json" diff --git a/lerna.json b/lerna.json index e3c3f83f3096..d36af7896191 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "8.26.0", + "version": "8.27.0", "npmClient": "yarn" } diff --git a/packages/angular/package.json b/packages/angular/package.json index a6c6bb06ef94..10a61dbeaeb1 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/angular", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Angular", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/angular", @@ -21,10 +21,10 @@ "rxjs": "^6.5.5 || ^7.x" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "tslib": "^2.4.1" }, "devDependencies": { diff --git a/packages/astro/package.json b/packages/astro/package.json index b5fd94e25d3a..4b7c80993dfb 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/astro", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Astro", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/astro", @@ -56,11 +56,11 @@ "astro": ">=3.x || >=4.0.0-beta" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@sentry/vite-plugin": "^2.20.1" }, "devDependencies": { diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index 881976a8db34..418e2a64a56a 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/aws-serverless", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for AWS Lambda and AWS Serverless Environments", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/serverless", @@ -66,10 +66,10 @@ "dependencies": { "@opentelemetry/instrumentation-aws-lambda": "0.43.0", "@opentelemetry/instrumentation-aws-sdk": "0.43.1", - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@types/aws-lambda": "^8.10.62" }, "devDependencies": { diff --git a/packages/browser-utils/package.json b/packages/browser-utils/package.json index 235032606e5d..5c6a0ba6ba00 100644 --- a/packages/browser-utils/package.json +++ b/packages/browser-utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-utils", - "version": "8.26.0", + "version": "8.27.0", "description": "Browser Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser-utils", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/browser/package.json b/packages/browser/package.json index 07322e621598..736a3a0349a5 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/browser", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for browsers", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser", @@ -39,16 +39,16 @@ "access": "public" }, "dependencies": { - "@sentry-internal/browser-utils": "8.26.0", - "@sentry-internal/feedback": "8.26.0", - "@sentry-internal/replay": "8.26.0", - "@sentry-internal/replay-canvas": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry-internal/browser-utils": "8.27.0", + "@sentry-internal/feedback": "8.27.0", + "@sentry-internal/replay": "8.27.0", + "@sentry-internal/replay-canvas": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "devDependencies": { - "@sentry-internal/integration-shims": "8.26.0", + "@sentry-internal/integration-shims": "8.27.0", "fake-indexeddb": "^4.0.1" }, "scripts": { diff --git a/packages/bun/package.json b/packages/bun/package.json index fe0c181bf70a..3c730acc137f 100644 --- a/packages/bun/package.json +++ b/packages/bun/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/bun", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for bun", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/bun", @@ -39,11 +39,11 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "devDependencies": { "bun-types": "latest" diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json index fd9d9bcffe36..ed5ca89b8094 100644 --- a/packages/cloudflare/package.json +++ b/packages/cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/cloudflare", - "version": "8.26.0", + "version": "8.27.0", "description": "Offical Sentry SDK for Cloudflare Workers and Pages", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/cloudflare", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "optionalDependencies": { "@cloudflare/workers-types": "^4.x" diff --git a/packages/core/package.json b/packages/core/package.json index 8f140bfbb09c..a9bd50bc0fba 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/core", - "version": "8.26.0", + "version": "8.27.0", "description": "Base implementation for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/core", @@ -39,8 +39,8 @@ "access": "public" }, "dependencies": { - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/deno/package.json b/packages/deno/package.json index 4178c41cff65..2e45b6cab9a7 100644 --- a/packages/deno/package.json +++ b/packages/deno/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/deno", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Deno", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/deno", @@ -24,9 +24,9 @@ "/build" ], "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "devDependencies": { "@rollup/plugin-typescript": "^11.1.5", diff --git a/packages/ember/package.json b/packages/ember/package.json index 046d187d8da3..86cee9a07704 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/ember", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Ember.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/ember", @@ -33,10 +33,10 @@ "dependencies": { "@babel/core": "^7.24.4", "@embroider/macros": "^1.16.0", - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "ember-auto-import": "^2.7.2", "ember-cli-babel": "^8.2.0", "ember-cli-htmlbars": "^6.1.1", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index f7d26ab13625..9150ae217cb9 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-config-sdk", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK eslint config", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-config-sdk", @@ -22,8 +22,8 @@ "access": "public" }, "dependencies": { - "@sentry-internal/eslint-plugin-sdk": "8.26.0", - "@sentry-internal/typescript": "8.26.0", + "@sentry-internal/eslint-plugin-sdk": "8.27.0", + "@sentry-internal/typescript": "8.27.0", "@typescript-eslint/eslint-plugin": "^5.48.0", "@typescript-eslint/parser": "^5.48.0", "eslint-config-prettier": "^6.11.0", diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index 6f01f96eee3e..9d947d2c3f03 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-plugin-sdk", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK eslint plugin", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-plugin-sdk", diff --git a/packages/feedback/package.json b/packages/feedback/package.json index ff4c10fcad8a..d69f42e7d896 100644 --- a/packages/feedback/package.json +++ b/packages/feedback/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/feedback", - "version": "8.26.0", + "version": "8.27.0", "description": "Sentry SDK integration for user feedback", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/feedback", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "devDependencies": { "preact": "^10.19.4" diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 4bfd08a2dc80..b588a4daa156 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/gatsby", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Gatsby.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/gatsby", @@ -45,10 +45,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/react": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/react": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@sentry/webpack-plugin": "2.16.0" }, "peerDependencies": { diff --git a/packages/google-cloud-serverless/package.json b/packages/google-cloud-serverless/package.json index d8bcc758752b..2d5e364984cd 100644 --- a/packages/google-cloud-serverless/package.json +++ b/packages/google-cloud-serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/google-cloud-serverless", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Google Cloud Functions", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/google-cloud", @@ -48,10 +48,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@types/express": "^4.17.14" }, "devDependencies": { diff --git a/packages/integration-shims/package.json b/packages/integration-shims/package.json index 88eee4b1175a..ff8dfc0f155b 100644 --- a/packages/integration-shims/package.json +++ b/packages/integration-shims/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/integration-shims", - "version": "8.26.0", + "version": "8.27.0", "description": "Shims for integrations in Sentry SDK.", "main": "build/cjs/index.js", "module": "build/esm/index.js", @@ -55,9 +55,9 @@ "url": "https://github.com/getsentry/sentry-javascript/issues" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "engines": { "node": ">=14.18" diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index 902b89d11591..9d084af2a92f 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nestjs", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for NestJS", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nestjs", @@ -44,10 +44,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "devDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 466a98f8512f..593a23c5d133 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nextjs", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Next.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nextjs", @@ -71,13 +71,13 @@ "@opentelemetry/instrumentation-http": "0.52.1", "@opentelemetry/semantic-conventions": "^1.25.1", "@rollup/plugin-commonjs": "26.0.1", - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/react": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", - "@sentry/vercel-edge": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/react": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", + "@sentry/vercel-edge": "8.27.0", "@sentry/webpack-plugin": "2.20.1", "chalk": "3.0.0", "resolve": "1.22.8", diff --git a/packages/node/package.json b/packages/node/package.json index fa8fa1a797c7..e6a1b0a7feec 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/node", - "version": "8.26.0", + "version": "8.27.0", "description": "Sentry Node SDK using OpenTelemetry for performance instrumentation", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/node", @@ -89,10 +89,10 @@ "@opentelemetry/sdk-trace-base": "^1.25.1", "@opentelemetry/semantic-conventions": "^1.25.1", "@prisma/instrumentation": "5.18.0", - "@sentry/core": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "import-in-the-middle": "^1.11.0" }, "devDependencies": { diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 2216991b5e6e..bf6e48bbf1d7 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nuxt", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Nuxt (EXPERIMENTAL)", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nuxt", @@ -43,14 +43,14 @@ }, "dependencies": { "@nuxt/kit": "^3.12.2", - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@sentry/vite-plugin": "2.20.1", - "@sentry/vue": "8.26.0" + "@sentry/vue": "8.27.0" }, "devDependencies": { "@nuxt/module-builder": "0.8.1", diff --git a/packages/opentelemetry/package.json b/packages/opentelemetry/package.json index 1ffc241172f9..34c498aa74c5 100644 --- a/packages/opentelemetry/package.json +++ b/packages/opentelemetry/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/opentelemetry", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry utilities for OpenTelemetry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/opentelemetry", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", diff --git a/packages/profiling-node/package.json b/packages/profiling-node/package.json index a779edc4e732..42795df3d0bc 100644 --- a/packages/profiling-node/package.json +++ b/packages/profiling-node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/profiling-node", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Node.js Profiling", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/profiling-node", @@ -75,10 +75,10 @@ "test": "cross-env SENTRY_PROFILER_BINARY_DIR=lib jest --config jest.config.js" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "detect-libc": "^2.0.2", "node-abi": "^3.61.0" }, diff --git a/packages/react/package.json b/packages/react/package.json index 9f2c8d5c3c12..f5e11fe94f99 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/react", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for React.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/react", @@ -39,10 +39,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "hoist-non-react-statics": "^3.3.2" }, "peerDependencies": { diff --git a/packages/remix/package.json b/packages/remix/package.json index 120f4592d94a..a7ba4b4dae0b 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/remix", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Remix", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/remix", @@ -55,12 +55,12 @@ "@opentelemetry/instrumentation-http": "0.52.1", "@remix-run/router": "1.x", "@sentry/cli": "^2.33.0", - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/react": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/react": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "glob": "^10.3.4", "opentelemetry-instrumentation-remix": "0.7.1", "yargs": "^17.6.0" diff --git a/packages/replay-canvas/package.json b/packages/replay-canvas/package.json index 7bfeb7304d01..1c3a8c202286 100644 --- a/packages/replay-canvas/package.json +++ b/packages/replay-canvas/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/replay-canvas", - "version": "8.26.0", + "version": "8.27.0", "description": "Replay canvas integration", "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", @@ -68,10 +68,10 @@ "@sentry-internal/rrweb": "2.26.0" }, "dependencies": { - "@sentry-internal/replay": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry-internal/replay": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "engines": { "node": ">=14.18" diff --git a/packages/replay-internal/package.json b/packages/replay-internal/package.json index 49ef6631c4d4..d7b602af7dc1 100644 --- a/packages/replay-internal/package.json +++ b/packages/replay-internal/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/replay", - "version": "8.26.0", + "version": "8.27.0", "description": "User replays for Sentry", "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", @@ -68,7 +68,7 @@ "homepage": "https://docs.sentry.io/platforms/javascript/session-replay/", "devDependencies": { "@babel/core": "^7.17.5", - "@sentry-internal/replay-worker": "8.26.0", + "@sentry-internal/replay-worker": "8.27.0", "@sentry-internal/rrweb": "2.26.0", "@sentry-internal/rrweb-snapshot": "2.26.0", "fflate": "^0.8.1", @@ -76,10 +76,10 @@ "jsdom-worker": "^0.2.1" }, "dependencies": { - "@sentry-internal/browser-utils": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry-internal/browser-utils": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "engines": { "node": ">=14.18" diff --git a/packages/replay-worker/package.json b/packages/replay-worker/package.json index 337490ecd4f2..d62c06034c49 100644 --- a/packages/replay-worker/package.json +++ b/packages/replay-worker/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/replay-worker", - "version": "8.26.0", + "version": "8.27.0", "description": "Worker for @sentry-internal/replay", "main": "build/esm/index.js", "module": "build/esm/index.js", diff --git a/packages/solid/package.json b/packages/solid/package.json index e8c7547014c0..be3e51a131c4 100644 --- a/packages/solid/package.json +++ b/packages/solid/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/solid", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Solid", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/solid", @@ -44,10 +44,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "peerDependencies": { "@solidjs/router": "^0.13.4", diff --git a/packages/solidstart/package.json b/packages/solidstart/package.json index 3251549a62b5..ee9fd2e4c956 100644 --- a/packages/solidstart/package.json +++ b/packages/solidstart/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/solidstart", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Solid Start", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/solidstart", @@ -78,12 +78,12 @@ }, "dependencies": { "@opentelemetry/instrumentation": "^0.52.1", - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/solid": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/solid": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@sentry/vite-plugin": "2.19.0" }, "devDependencies": { diff --git a/packages/svelte/package.json b/packages/svelte/package.json index cd01ba37e5bc..c03f0ba2ec48 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/svelte", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Svelte", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/svelte", @@ -39,10 +39,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "magic-string": "^0.30.0" }, "peerDependencies": { diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index a3df07e38eff..f332530c338c 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/sveltekit", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for SvelteKit", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/sveltekit", @@ -40,12 +40,12 @@ } }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/node": "8.26.0", - "@sentry/opentelemetry": "8.26.0", - "@sentry/svelte": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0", + "@sentry/core": "8.27.0", + "@sentry/node": "8.27.0", + "@sentry/opentelemetry": "8.27.0", + "@sentry/svelte": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0", "@sentry/vite-plugin": "2.22.0", "magic-string": "0.30.7", "magicast": "0.2.8", diff --git a/packages/types/package.json b/packages/types/package.json index 3d649c4f3c0f..5b92d1766c7d 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/types", - "version": "8.26.0", + "version": "8.27.0", "description": "Types for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types", diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 890db27ce2f2..165b6db47a79 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/typescript", - "version": "8.26.0", + "version": "8.27.0", "description": "Typescript configuration used at Sentry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/typescript", diff --git a/packages/utils/package.json b/packages/utils/package.json index f9aaffaeaf56..ffd3cae5b3c5 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/utils", - "version": "8.26.0", + "version": "8.27.0", "description": "Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/utils", @@ -39,7 +39,7 @@ "access": "public" }, "dependencies": { - "@sentry/types": "8.26.0" + "@sentry/types": "8.27.0" }, "devDependencies": { "@types/array.prototype.flat": "^1.2.1", diff --git a/packages/utils/src/version.ts b/packages/utils/src/version.ts index 1b32cd60a8c4..424a201c405f 100644 --- a/packages/utils/src/version.ts +++ b/packages/utils/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = '8.26.0'; +export const SDK_VERSION = '8.27.0'; diff --git a/packages/vercel-edge/package.json b/packages/vercel-edge/package.json index 93c6b9e46201..09f9860418ae 100644 --- a/packages/vercel-edge/package.json +++ b/packages/vercel-edge/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vercel-edge", - "version": "8.26.0", + "version": "8.27.0", "description": "Offical Sentry SDK for the Vercel Edge Runtime", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vercel-edge", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "devDependencies": { "@edge-runtime/types": "3.0.1" diff --git a/packages/vue/package.json b/packages/vue/package.json index dbb639eccc40..f1604d24e948 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vue", - "version": "8.26.0", + "version": "8.27.0", "description": "Official Sentry SDK for Vue.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vue", @@ -39,10 +39,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "peerDependencies": { "vue": "2.x || 3.x" diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 36d7116050f4..5626e5096152 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/wasm", - "version": "8.26.0", + "version": "8.27.0", "description": "Support for WASM.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/wasm", @@ -39,10 +39,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.26.0", - "@sentry/core": "8.26.0", - "@sentry/types": "8.26.0", - "@sentry/utils": "8.26.0" + "@sentry/browser": "8.27.0", + "@sentry/core": "8.27.0", + "@sentry/types": "8.27.0", + "@sentry/utils": "8.27.0" }, "scripts": { "build": "run-p build:transpile build:bundle build:types", From 2cbf64e6c735d1dad3629e2abcb8688b8950409f Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 28 Aug 2024 10:59:34 +0200 Subject: [PATCH 14/64] ref(browser): Use `Proxy` for XHR instrumentation (#12212) --- packages/browser-utils/src/instrument/xhr.ts | 78 +++++++++++--------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/packages/browser-utils/src/instrument/xhr.ts b/packages/browser-utils/src/instrument/xhr.ts index 5b7a9c261092..c46662bf7c16 100644 --- a/packages/browser-utils/src/instrument/xhr.ts +++ b/packages/browser-utils/src/instrument/xhr.ts @@ -1,6 +1,6 @@ -import type { HandlerDataXhr, SentryWrappedXMLHttpRequest, WrappedFunction } from '@sentry/types'; +import type { HandlerDataXhr, SentryWrappedXMLHttpRequest } from '@sentry/types'; -import { addHandler, fill, isString, maybeInstrument, timestampInSeconds, triggerHandlers } from '@sentry/utils'; +import { addHandler, isString, maybeInstrument, timestampInSeconds, triggerHandlers } from '@sentry/utils'; import { WINDOW } from '../types'; export const SENTRY_XHR_DATA_KEY = '__sentry_xhr_v3__'; @@ -29,20 +29,21 @@ export function instrumentXHR(): void { const xhrproto = XMLHttpRequest.prototype; - fill(xhrproto, 'open', function (originalOpen: () => void): () => void { - return function (this: XMLHttpRequest & SentryWrappedXMLHttpRequest, ...args: unknown[]): void { + // eslint-disable-next-line @typescript-eslint/unbound-method + xhrproto.open = new Proxy(xhrproto.open, { + apply(originalOpen, xhrOpenThisArg: XMLHttpRequest & SentryWrappedXMLHttpRequest, xhrOpenArgArray) { const startTimestamp = timestampInSeconds() * 1000; // open() should always be called with two or more arguments // But to be on the safe side, we actually validate this and bail out if we don't have a method & url - const method = isString(args[0]) ? args[0].toUpperCase() : undefined; - const url = parseUrl(args[1]); + const method = isString(xhrOpenArgArray[0]) ? xhrOpenArgArray[0].toUpperCase() : undefined; + const url = parseUrl(xhrOpenArgArray[1]); if (!method || !url) { - return originalOpen.apply(this, args); + return originalOpen.apply(xhrOpenThisArg, xhrOpenArgArray); } - this[SENTRY_XHR_DATA_KEY] = { + xhrOpenThisArg[SENTRY_XHR_DATA_KEY] = { method, url, request_headers: {}, @@ -50,22 +51,22 @@ export function instrumentXHR(): void { // if Sentry key appears in URL, don't capture it as a request if (method === 'POST' && url.match(/sentry_key/)) { - this.__sentry_own_request__ = true; + xhrOpenThisArg.__sentry_own_request__ = true; } const onreadystatechangeHandler: () => void = () => { // For whatever reason, this is not the same instance here as from the outer method - const xhrInfo = this[SENTRY_XHR_DATA_KEY]; + const xhrInfo = xhrOpenThisArg[SENTRY_XHR_DATA_KEY]; if (!xhrInfo) { return; } - if (this.readyState === 4) { + if (xhrOpenThisArg.readyState === 4) { try { // touching statusCode in some platforms throws // an exception - xhrInfo.status_code = this.status; + xhrInfo.status_code = xhrOpenThisArg.status; } catch (e) { /* do nothing */ } @@ -73,64 +74,69 @@ export function instrumentXHR(): void { const handlerData: HandlerDataXhr = { endTimestamp: timestampInSeconds() * 1000, startTimestamp, - xhr: this, + xhr: xhrOpenThisArg, }; triggerHandlers('xhr', handlerData); } }; - if ('onreadystatechange' in this && typeof this.onreadystatechange === 'function') { - fill(this, 'onreadystatechange', function (original: WrappedFunction) { - return function (this: SentryWrappedXMLHttpRequest, ...readyStateArgs: unknown[]): void { + if ('onreadystatechange' in xhrOpenThisArg && typeof xhrOpenThisArg.onreadystatechange === 'function') { + xhrOpenThisArg.onreadystatechange = new Proxy(xhrOpenThisArg.onreadystatechange, { + apply(originalOnreadystatechange, onreadystatechangeThisArg, onreadystatechangeArgArray: unknown[]) { onreadystatechangeHandler(); - return original.apply(this, readyStateArgs); - }; + return originalOnreadystatechange.apply(onreadystatechangeThisArg, onreadystatechangeArgArray); + }, }); } else { - this.addEventListener('readystatechange', onreadystatechangeHandler); + xhrOpenThisArg.addEventListener('readystatechange', onreadystatechangeHandler); } // Intercepting `setRequestHeader` to access the request headers of XHR instance. // This will only work for user/library defined headers, not for the default/browser-assigned headers. // Request cookies are also unavailable for XHR, as `Cookie` header can't be defined by `setRequestHeader`. - fill(this, 'setRequestHeader', function (original: WrappedFunction) { - return function (this: SentryWrappedXMLHttpRequest, ...setRequestHeaderArgs: unknown[]): void { - const [header, value] = setRequestHeaderArgs; + xhrOpenThisArg.setRequestHeader = new Proxy(xhrOpenThisArg.setRequestHeader, { + apply( + originalSetRequestHeader, + setRequestHeaderThisArg: SentryWrappedXMLHttpRequest, + setRequestHeaderArgArray: unknown[], + ) { + const [header, value] = setRequestHeaderArgArray; - const xhrInfo = this[SENTRY_XHR_DATA_KEY]; + const xhrInfo = setRequestHeaderThisArg[SENTRY_XHR_DATA_KEY]; if (xhrInfo && isString(header) && isString(value)) { xhrInfo.request_headers[header.toLowerCase()] = value; } - return original.apply(this, setRequestHeaderArgs); - }; + return originalSetRequestHeader.apply(setRequestHeaderThisArg, setRequestHeaderArgArray); + }, }); - return originalOpen.apply(this, args); - }; + return originalOpen.apply(xhrOpenThisArg, xhrOpenArgArray); + }, }); - fill(xhrproto, 'send', function (originalSend: () => void): () => void { - return function (this: XMLHttpRequest & SentryWrappedXMLHttpRequest, ...args: unknown[]): void { - const sentryXhrData = this[SENTRY_XHR_DATA_KEY]; + // eslint-disable-next-line @typescript-eslint/unbound-method + xhrproto.send = new Proxy(xhrproto.send, { + apply(originalSend, sendThisArg: XMLHttpRequest & SentryWrappedXMLHttpRequest, sendArgArray: unknown[]) { + const sentryXhrData = sendThisArg[SENTRY_XHR_DATA_KEY]; if (!sentryXhrData) { - return originalSend.apply(this, args); + return originalSend.apply(sendThisArg, sendArgArray); } - if (args[0] !== undefined) { - sentryXhrData.body = args[0]; + if (sendArgArray[0] !== undefined) { + sentryXhrData.body = sendArgArray[0]; } const handlerData: HandlerDataXhr = { startTimestamp: timestampInSeconds() * 1000, - xhr: this, + xhr: sendThisArg, }; triggerHandlers('xhr', handlerData); - return originalSend.apply(this, args); - }; + return originalSend.apply(sendThisArg, sendArgArray); + }, }); } From 14e56c7ef01705e960d563069f2974d0ca6d240d Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 28 Aug 2024 11:10:22 +0200 Subject: [PATCH 15/64] ci: Update actions/upload-artifact to v4 (#13489) It keeps complaining that this is deprecated, so bumping this to v4. --- .github/workflows/build.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9ea899390b33..3798b82094b3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -185,7 +185,7 @@ jobs: with: name: build-output path: ${{ env.CACHED_BUILD_PATHS }} - retention-days: 7 + retention-days: 4 compression-level: 6 overwrite: true @@ -345,6 +345,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{ github.sha }} + retention-days: 90 path: | ${{ github.workspace }}/packages/browser/build/bundles/** ${{ github.workspace }}/packages/replay-internal/build/bundles/** @@ -597,11 +598,13 @@ jobs: run: yarn test:ci${{ matrix.project && format(' --project={0}', matrix.project) || '' }}${{ matrix.shard && format(' --shard={0}/{1}', matrix.shard, matrix.shards) || '' }} - name: Upload Playwright Traces - uses: actions/upload-artifact@v3 - if: always() + uses: actions/upload-artifact@v4 + if: failure() with: - name: playwright-traces + name: playwright-traces-job_browser_playwright_tests-${{ matrix.bundle}}-${{matrix.project}}-${{matrix.shard || '0'}} path: dev-packages/browser-integration-tests/test-results + overwrite: true + retention-days: 7 job_browser_loader_tests: name: PW ${{ matrix.bundle }} Tests @@ -647,11 +650,13 @@ jobs: cd dev-packages/browser-integration-tests yarn test:loader - name: Upload Playwright Traces - uses: actions/upload-artifact@v3 - if: always() + uses: actions/upload-artifact@v4 + if: failure() with: - name: playwright-traces + name: playwright-traces-job_browser_loader_tests-${{ matrix.bundle}} path: dev-packages/browser-integration-tests/test-results + overwrite: true + retention-days: 7 job_check_for_faulty_dts: name: Check for faulty .d.ts files @@ -1261,6 +1266,7 @@ jobs: with: name: ${{ steps.process.outputs.artifactName }} path: ${{ steps.process.outputs.artifactPath }} + retention-days: 7 job_compile_bindings_profiling_node: name: Compile & Test Profiling Bindings (v${{ matrix.node }}) ${{ matrix.target_platform || matrix.os }}, ${{ matrix.node || matrix.container }}, ${{ matrix.arch || matrix.container }}, ${{ contains(matrix.container, 'alpine') && 'musl' || 'glibc' }} From 977c5087293cce8c82787dfdd925d43576d3f6f9 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 28 Aug 2024 11:29:42 +0200 Subject: [PATCH 16/64] ci: Do not fail on missing dependency cache (#13492) This PR updates CI to never fail on cache misses. Instead, we rerun install or rebuild the tarballs, if necessary. One tricky aspect of this is that `yarn install` will fail when running on node 14, because some dependencies do not work with it. We "fix" this by temporarily installing the default node version in this case, run `yarn install`, then revert to node 14. --- .github/actions/restore-cache/action.yml | 27 ++++++- .github/workflows/build.yml | 97 ++++++++++++++---------- 2 files changed, 79 insertions(+), 45 deletions(-) diff --git a/.github/actions/restore-cache/action.yml b/.github/actions/restore-cache/action.yml index 316d89dc1c26..6cd63a6550e4 100644 --- a/.github/actions/restore-cache/action.yml +++ b/.github/actions/restore-cache/action.yml @@ -1,6 +1,14 @@ name: "Restore dependency & build cache" description: "Restore the dependency & build cache." +inputs: + dependency_cache_key: + description: "The dependency cache key" + required: true + node_version: + description: "If set, temporarily set node version to default one before installing, then revert to this version after." + required: false + runs: using: "composite" steps: @@ -9,15 +17,26 @@ runs: uses: actions/cache/restore@v4 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ env.DEPENDENCY_CACHE_KEY }} + key: ${{ inputs.dependency_cache_key }} - name: Restore build artifacts uses: actions/download-artifact@v4 with: name: build-output - - name: Check if caches are restored - uses: actions/github-script@v6 + - name: Use default node version for install + if: inputs.node_version && steps.dep-cache.outputs.cache-hit != 'true' + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + + - name: Install dependencies if: steps.dep-cache.outputs.cache-hit != 'true' + run: yarn install --ignore-engines --frozen-lockfile + shell: bash + + - name: Revert node version to ${{ inputs.node_version }} + if: inputs.node_version && steps.dep-cache.outputs.cache-hit != 'true' + uses: actions/setup-node@v4 with: - script: core.setFailed('Dependency cache could not be restored - please re-run ALL jobs.') + node-version: ${{ inputs.node_version }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3798b82094b3..576e2e7aa27c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -233,8 +233,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check bundle sizes uses: ./dev-packages/size-limit-gh-action with: @@ -260,8 +260,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Lint source files run: yarn lint:lerna - name: Lint C++ files @@ -306,8 +306,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run madge run: yarn circularDepCheck @@ -328,8 +328,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Extract Profiling Node Prebuilt Binaries uses: actions/download-artifact@v4 @@ -376,8 +376,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run affected tests run: yarn test:pr:browser --base=${{ github.event.pull_request.base.sha }} @@ -413,8 +413,8 @@ jobs: uses: oven-sh/setup-bun@v2 - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run tests run: | yarn test:ci:bun @@ -442,8 +442,8 @@ jobs: deno-version: v1.38.5 - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run tests run: | cd packages/deno @@ -476,8 +476,9 @@ jobs: node-version: ${{ matrix.node }} - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + node_version: ${{ matrix.node == 14 && '14' || '' }} - name: Run affected tests run: yarn test:pr:node --base=${{ github.event.pull_request.base.sha }} @@ -515,8 +516,8 @@ jobs: python-version: '3.11.7' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Build Configure node-gyp run: yarn lerna run build:bindings:configure --scope @sentry/profiling-node - name: Build Bindings for Current Environment @@ -583,8 +584,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Install Playwright uses: ./.github/actions/install-playwright @@ -635,8 +636,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Install Playwright uses: ./.github/actions/install-playwright @@ -674,8 +675,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check for dts files that reference stuff in the temporary build folder run: | if grep -r --include "*.d.ts" --exclude-dir ".nxcache" 'import("@sentry(-internal)?/[^/]*/build' .; then @@ -712,8 +713,9 @@ jobs: node-version: ${{ matrix.node }} - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + node_version: ${{ matrix.node == 14 && '14' || '' }} - name: Overwrite typescript version if: matrix.typescript @@ -753,8 +755,8 @@ jobs: node-version: ${{ matrix.node }} - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Install Playwright uses: ./.github/actions/install-playwright @@ -791,8 +793,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: NX cache uses: actions/cache/restore@v4 with: @@ -953,15 +955,19 @@ jobs: uses: oven-sh/setup-bun@v2 - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Restore tarball cache uses: actions/cache/restore@v4 + id: restore-tarball-cache with: path: ${{ github.workspace }}/packages/*/*.tgz key: ${{ env.BUILD_CACHE_TARBALL_KEY }} - fail-on-cache-miss: true + + - name: Build tarballs if not cached + if: steps.restore-tarball-cache.outputs.cache-hit != 'true' + run: yarn build:tarball - name: Install Playwright uses: ./.github/actions/install-playwright @@ -1053,15 +1059,19 @@ jobs: node-version-file: 'dev-packages/e2e-tests/package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Restore tarball cache uses: actions/cache/restore@v4 + id: restore-tarball-cache with: path: ${{ github.workspace }}/packages/*/*.tgz key: ${{ env.BUILD_CACHE_TARBALL_KEY }} - fail-on-cache-miss: true + + - name: Build tarballs if not cached + if: steps.restore-tarball-cache.outputs.cache-hit != 'true' + run: yarn build:tarball - name: Install Playwright uses: ./.github/actions/install-playwright @@ -1148,8 +1158,8 @@ jobs: node-version-file: 'dev-packages/e2e-tests/package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Build Profiling Node run: yarn lerna run build:lib --scope @sentry/profiling-node - name: Extract Profiling Node Prebuilt Binaries @@ -1158,12 +1168,17 @@ jobs: pattern: profiling-node-binaries-${{ github.sha }}-* path: ${{ github.workspace }}/packages/profiling-node/lib/ merge-multiple: true + - name: Restore tarball cache uses: actions/cache/restore@v4 + id: restore-tarball-cache with: path: ${{ github.workspace }}/packages/*/*.tgz key: ${{ env.BUILD_CACHE_TARBALL_KEY }} - fail-on-cache-miss : true + + - name: Build tarballs if not cached + if: steps.restore-tarball-cache.outputs.cache-hit != 'true' + run: yarn build:tarball - name: Install Playwright uses: ./.github/actions/install-playwright @@ -1244,8 +1259,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Collect run: yarn ci:collect From 3964548c0fcf74c03857ed4ff68af7070cdcb129 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 28 Aug 2024 11:53:21 +0200 Subject: [PATCH 17/64] fix(cdn): Fix SDK source for CDN bundles (#13475) We used to replace `__SENTRY_SDK_SOURCE__` when we built `@sentry/utils`, which means that we could not overwrite it anymore for the CDN bundles, resulting in the SDK source being `npm` for the CDN bundles. This PR changes this so that this is correct now. Closes https://github.com/getsentry/sentry-javascript/issues/13435 --- .size-limit.js | 2 +- .../loader/noOnLoad/captureException/test.ts | 20 ++++++++++++ .../captureException/simpleError/test.ts | 32 +++++++++++++++---- dev-packages/rollup-utils/npmHelpers.mjs | 3 -- .../rollup-utils/plugins/bundlePlugins.mjs | 3 +- packages/utils/src/env.ts | 4 +-- 6 files changed, 51 insertions(+), 13 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index 2280b950c513..e75eeb422735 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -170,7 +170,7 @@ module.exports = [ path: createCDNPath('bundle.tracing.min.js'), gzip: false, brotli: false, - limit: '111 KB', + limit: '113 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay) - uncompressed', diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/noOnLoad/captureException/test.ts b/dev-packages/browser-integration-tests/loader-suites/loader/noOnLoad/captureException/test.ts index 09a10464c22e..4404dac91364 100644 --- a/dev-packages/browser-integration-tests/loader-suites/loader/noOnLoad/captureException/test.ts +++ b/dev-packages/browser-integration-tests/loader-suites/loader/noOnLoad/captureException/test.ts @@ -1,4 +1,5 @@ import { expect } from '@playwright/test'; +import { SDK_VERSION } from '@sentry/browser'; import { sentryTest } from '../../../../utils/fixtures'; import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../../utils/helpers'; @@ -11,3 +12,22 @@ sentryTest('captureException works', async ({ getLocalTestUrl, page }) => { expect(eventData.message).toBe('Test exception'); }); + +sentryTest('should capture correct SDK metadata', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForErrorRequestOnUrl(page, url); + + const eventData = envelopeRequestParser(req); + + expect(eventData.sdk).toMatchObject({ + name: 'sentry.javascript.browser', + version: SDK_VERSION, + integrations: expect.any(Object), + packages: [ + { + name: 'loader:@sentry/browser', + version: SDK_VERSION, + }, + ], + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/captureException/simpleError/test.ts b/dev-packages/browser-integration-tests/suites/public-api/captureException/simpleError/test.ts index 7e884c6eb6dc..85f001849748 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/captureException/simpleError/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/captureException/simpleError/test.ts @@ -1,13 +1,13 @@ import { expect } from '@playwright/test'; -import type { Event } from '@sentry/types'; +import { SDK_VERSION } from '@sentry/browser'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; +import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../../utils/helpers'; -sentryTest('should capture a simple error with message', async ({ getLocalTestPath, page }) => { - const url = await getLocalTestPath({ testDir: __dirname }); - - const eventData = await getFirstSentryEnvelopeRequest(page, url); +sentryTest('should capture a simple error with message', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForErrorRequestOnUrl(page, url); + const eventData = envelopeRequestParser(req); expect(eventData.exception?.values).toHaveLength(1); expect(eventData.exception?.values?.[0]).toMatchObject({ @@ -22,3 +22,23 @@ sentryTest('should capture a simple error with message', async ({ getLocalTestPa }, }); }); + +sentryTest('should capture correct SDK metadata', async ({ getLocalTestUrl, page }) => { + const isCdn = (process.env.PW_BUNDLE || '').startsWith('bundle'); + + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForErrorRequestOnUrl(page, url); + const eventData = envelopeRequestParser(req); + + expect(eventData.sdk).toEqual({ + name: 'sentry.javascript.browser', + version: SDK_VERSION, + integrations: expect.any(Object), + packages: [ + { + name: `${isCdn ? 'cdn' : 'npm'}:@sentry/browser`, + version: SDK_VERSION, + }, + ], + }); +}); diff --git a/dev-packages/rollup-utils/npmHelpers.mjs b/dev-packages/rollup-utils/npmHelpers.mjs index 410d0847d928..1a855e5674b7 100644 --- a/dev-packages/rollup-utils/npmHelpers.mjs +++ b/dev-packages/rollup-utils/npmHelpers.mjs @@ -19,7 +19,6 @@ import { makeImportMetaUrlReplacePlugin, makeNodeResolvePlugin, makeRrwebBuildPlugin, - makeSetSDKSourcePlugin, makeSucrasePlugin, } from './plugins/index.mjs'; import { makePackageNodeEsm } from './plugins/make-esm-plugin.mjs'; @@ -45,7 +44,6 @@ export function makeBaseNPMConfig(options = {}) { const importMetaUrlReplacePlugin = makeImportMetaUrlReplacePlugin(); const cleanupPlugin = makeCleanupPlugin(); const extractPolyfillsPlugin = makeExtractPolyfillsPlugin(); - const setSdkSourcePlugin = makeSetSDKSourcePlugin('npm'); const rrwebBuildPlugin = makeRrwebBuildPlugin({ excludeShadowDom: undefined, excludeIframe: undefined, @@ -106,7 +104,6 @@ export function makeBaseNPMConfig(options = {}) { plugins: [ nodeResolvePlugin, - setSdkSourcePlugin, sucrasePlugin, debugBuildStatementReplacePlugin, importMetaUrlReplacePlugin, diff --git a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs index 2ce35d1e6168..a3e25c232479 100644 --- a/dev-packages/rollup-utils/plugins/bundlePlugins.mjs +++ b/dev-packages/rollup-utils/plugins/bundlePlugins.mjs @@ -63,8 +63,9 @@ export function makeIsDebugBuildPlugin(includeDebugging) { export function makeSetSDKSourcePlugin(sdkSource) { return replace({ preventAssignment: false, + delimiters: ['', ''], values: { - __SENTRY_SDK_SOURCE__: JSON.stringify(sdkSource), + '/* __SENTRY_SDK_SOURCE__ */': `return ${JSON.stringify(sdkSource)};`, }, }); } diff --git a/packages/utils/src/env.ts b/packages/utils/src/env.ts index 0a3308a88561..b85c91c55a8d 100644 --- a/packages/utils/src/env.ts +++ b/packages/utils/src/env.ts @@ -30,6 +30,6 @@ export function isBrowserBundle(): boolean { * Get source of SDK. */ export function getSDKSource(): SdkSource { - // @ts-expect-error __SENTRY_SDK_SOURCE__ is injected by rollup during build process - return __SENTRY_SDK_SOURCE__; + // This comment is used to identify this line in the CDN bundle build step and replace this with "return 'cdn';" + /* __SENTRY_SDK_SOURCE__ */ return 'npm'; } From 2b3e26f26a7303ef6e33aea6c0891feef50891a3 Mon Sep 17 00:00:00 2001 From: Andrei <168741329+andreiborza@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:02:50 +0200 Subject: [PATCH 18/64] ref(solidstart): Simplify middleware structure without subexport (#13494) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⚠️ This is a breaking change ⚠️ Simplifies the package by getting rid of the `@sentry/solidstart/middleware` subexport in favor of a regular export. The middleware now needs to be imported like this: ```js import { sentryBeforeResponseMiddleware } from '@sentry/solidstart'; ``` --- packages/solidstart/README.md | 4 ++-- packages/solidstart/package.json | 13 +------------ packages/solidstart/rollup.npm.config.mjs | 1 - packages/solidstart/src/server/index.ts | 1 + packages/solidstart/src/{ => server}/middleware.ts | 0 .../solidstart/test/{ => server}/middleware.test.ts | 4 ++-- packages/solidstart/tsconfig.subexports-types.json | 1 - packages/solidstart/tsconfig.types.json | 1 - 8 files changed, 6 insertions(+), 19 deletions(-) rename packages/solidstart/src/{ => server}/middleware.ts (100%) rename packages/solidstart/test/{ => server}/middleware.test.ts (95%) diff --git a/packages/solidstart/README.md b/packages/solidstart/README.md index b5d775781875..0b25a3a37e3e 100644 --- a/packages/solidstart/README.md +++ b/packages/solidstart/README.md @@ -73,10 +73,10 @@ Sentry.init({ ### 4. Server instrumentation -Complete the setup by adding the Sentry middlware to your `src/middleware.ts` file: +Complete the setup by adding the Sentry middleware to your `src/middleware.ts` file: ```typescript -import { sentryBeforeResponseMiddleware } from '@sentry/solidstart/middleware'; +import { sentryBeforeResponseMiddleware } from '@sentry/solidstart'; import { createMiddleware } from '@solidjs/start/middleware'; export default createMiddleware({ diff --git a/packages/solidstart/package.json b/packages/solidstart/package.json index ee9fd2e4c956..4fb81cc25c2f 100644 --- a/packages/solidstart/package.json +++ b/packages/solidstart/package.json @@ -39,17 +39,6 @@ "require": "./build/cjs/index.server.js" } }, - "./middleware": { - "types": "./middleware.d.ts", - "import": { - "types": "./middleware.d.ts", - "default": "./build/esm/middleware.js" - }, - "require": { - "types": "./middleware.d.ts", - "default": "./build/cjs/middleware.js" - } - }, "./solidrouter": { "types": "./solidrouter.d.ts", "browser": { @@ -106,7 +95,7 @@ "build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:tarball": "npm pack", - "circularDepCheck": "madge --circular src/index.client.ts && madge --circular src/index.server.ts && madge --circular src/index.types.ts && madge --circular src/solidrouter.client.ts && madge --circular src/solidrouter.server.ts && madge --circular src/solidrouter.ts && madge --circular src/middleware.ts", + "circularDepCheck": "madge --circular src/index.client.ts && madge --circular src/index.server.ts && madge --circular src/index.types.ts && madge --circular src/solidrouter.client.ts && madge --circular src/solidrouter.server.ts && madge --circular src/solidrouter.ts", "clean": "rimraf build coverage sentry-solidstart-*.tgz ./*.d.ts ./*.d.ts.map ./client ./server", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", diff --git a/packages/solidstart/rollup.npm.config.mjs b/packages/solidstart/rollup.npm.config.mjs index 8e91d0371a27..b0087a93c6fe 100644 --- a/packages/solidstart/rollup.npm.config.mjs +++ b/packages/solidstart/rollup.npm.config.mjs @@ -12,7 +12,6 @@ export default makeNPMConfigVariants( 'src/solidrouter.server.ts', 'src/client/solidrouter.ts', 'src/server/solidrouter.ts', - 'src/middleware.ts', ], // prevent this internal code from ending up in our built package (this doesn't happen automatially because // the name doesn't match an SDK dependency) diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index 75a67d3bb847..c3499a82459a 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -128,3 +128,4 @@ export { withSentryErrorBoundary } from '@sentry/solid'; export { init } from './sdk'; export * from './withServerActionInstrumentation'; +export * from './middleware'; diff --git a/packages/solidstart/src/middleware.ts b/packages/solidstart/src/server/middleware.ts similarity index 100% rename from packages/solidstart/src/middleware.ts rename to packages/solidstart/src/server/middleware.ts diff --git a/packages/solidstart/test/middleware.test.ts b/packages/solidstart/test/server/middleware.test.ts similarity index 95% rename from packages/solidstart/test/middleware.test.ts rename to packages/solidstart/test/server/middleware.test.ts index c025dc38af97..c1d6ff644b9d 100644 --- a/packages/solidstart/test/middleware.test.ts +++ b/packages/solidstart/test/server/middleware.test.ts @@ -1,7 +1,7 @@ import * as SentryCore from '@sentry/core'; import { beforeEach, describe, it, vi } from 'vitest'; -import { sentryBeforeResponseMiddleware } from '../src/middleware'; -import type { ResponseMiddlewareResponse } from '../src/middleware'; +import { sentryBeforeResponseMiddleware } from '../../src/server'; +import type { ResponseMiddlewareResponse } from '../../src/server'; describe('middleware', () => { describe('sentryBeforeResponseMiddleware', () => { diff --git a/packages/solidstart/tsconfig.subexports-types.json b/packages/solidstart/tsconfig.subexports-types.json index 1c9daec11314..f800d830c511 100644 --- a/packages/solidstart/tsconfig.subexports-types.json +++ b/packages/solidstart/tsconfig.subexports-types.json @@ -15,7 +15,6 @@ "src/solidrouter.server.ts", "src/server/solidrouter.ts", "src/solidrouter.ts", - "src/middleware.ts", ], // Without this, we cannot output into the root dir "exclude": [] diff --git a/packages/solidstart/tsconfig.types.json b/packages/solidstart/tsconfig.types.json index bf2ca092abc1..51154c9c7878 100644 --- a/packages/solidstart/tsconfig.types.json +++ b/packages/solidstart/tsconfig.types.json @@ -15,6 +15,5 @@ "src/solidrouter.server.ts", "src/server/solidrouter.ts", "src/solidrouter.ts", - "src/middleware.ts", ] } From 80894c40440094185676e12a033f36acce67ac51 Mon Sep 17 00:00:00 2001 From: Leopold Kristjansson Date: Wed, 28 Aug 2024 13:42:12 +0200 Subject: [PATCH 19/64] Fix config example in README.md for Nuxt (#13496) Config example fix. env.DSN was missing process prefix. --- packages/nuxt/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/README.md b/packages/nuxt/README.md index 1227aa5e2c82..9049ec7e6691 100644 --- a/packages/nuxt/README.md +++ b/packages/nuxt/README.md @@ -96,7 +96,7 @@ Add a `sentry.client.config.(js|ts)` file to the root of your project: import * as Sentry from '@sentry/nuxt'; Sentry.init({ - dsn: env.DSN, + dsn: process.env.DSN, }); ``` From 664d30532dd619803e5cec39d11c956c11ca1867 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 28 Aug 2024 13:42:36 +0200 Subject: [PATCH 20/64] test: Increase timeout for redis-cache test & docker-compose (#13499) Noticed e.g. here: https://github.com/getsentry/sentry-javascript/actions/runs/10594383971/job/29358138105 that this was timing out some times, so increasing the timeout a bit... --- .../node-integration-tests/suites/tracing/redis-cache/test.ts | 2 +- dev-packages/node-integration-tests/utils/runner.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts b/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts index 8d494986ab3b..2b93c4d772bc 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts @@ -1,7 +1,7 @@ import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; // When running docker compose, we need a larger timeout, as this takes some time... -jest.setTimeout(75000); +jest.setTimeout(90000); describe('redis cache auto instrumentation', () => { afterAll(() => { diff --git a/dev-packages/node-integration-tests/utils/runner.ts b/dev-packages/node-integration-tests/utils/runner.ts index cb4ab58347e7..76c19074ed9c 100644 --- a/dev-packages/node-integration-tests/utils/runner.ts +++ b/dev-packages/node-integration-tests/utils/runner.ts @@ -124,7 +124,7 @@ async function runDockerCompose(options: DockerOptions): Promise { const timeout = setTimeout(() => { close(); reject(new Error('Timed out waiting for docker-compose')); - }, 60_000); + }, 75_000); function newData(data: Buffer): void { const text = data.toString('utf8'); From 3158c21198319d3fd8459a2fd415372449035fd1 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 28 Aug 2024 13:42:46 +0200 Subject: [PATCH 21/64] ci: Ensure cache save happens after install step (#13497) Noticed e.g. here: https://github.com/getsentry/sentry-javascript/actions/runs/10594383971/job/29359100222 that saving of the cache was not working. I guess this only works for the combined restore/save step, but here it expects that the cached data is there immediately (which makes sense!). --- .github/actions/install-playwright/action.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/actions/install-playwright/action.yml b/.github/actions/install-playwright/action.yml index 9de6e1a2b104..8fc0aeba7330 100644 --- a/.github/actions/install-playwright/action.yml +++ b/.github/actions/install-playwright/action.yml @@ -21,15 +21,6 @@ runs: ~/.cache/ms-playwright key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} - # Only store cache on develop branch - - name: Store cached playwright binaries - uses: actions/cache/save@v4 - if: github.event_name == 'push' && github.ref == 'refs/heads/develop' - with: - path: | - ~/.cache/ms-playwright - key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} - # We always install all browsers, if uncached - name: Install Playwright dependencies (uncached) run: npx playwright install chromium webkit firefox --with-deps @@ -40,3 +31,12 @@ runs: run: npx playwright install-deps ${{ inputs.browsers || 'chromium webkit firefox' }} if: steps.playwright-cache.outputs.cache-hit == 'true' shell: bash + + # Only store cache on develop branch + - name: Store cached playwright binaries + uses: actions/cache/save@v4 + if: github.event_name == 'push' && github.ref == 'refs/heads/develop' + with: + path: | + ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} From 9e56c74b2a945ea5c4c33d5fe5c1466e8844e1ad Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 28 Aug 2024 14:26:40 +0200 Subject: [PATCH 22/64] fix(node): Suppress tracing for transport request execution rather than transport creation (#13491) https://github.com/getsentry/sentry-javascript/issues/13466#issuecomment-2312856449 correctly points out that we are suppressing tracing for the transport creation instead of the transport execution. This PR wraps the code that is actually conducting the request with `suppressTracing` instead of the transport creation. Fixes https://github.com/getsentry/sentry-javascript/issues/13466 --- packages/browser/src/transports/fetch.ts | 1 + packages/cloudflare/src/transport.ts | 20 ++-- packages/deno/src/transports/index.ts | 20 ++-- packages/node/src/integrations/http.ts | 5 - packages/node/src/transports/http.ts | 108 ++++++++++--------- packages/vercel-edge/src/transports/index.ts | 20 ++-- 6 files changed, 89 insertions(+), 85 deletions(-) diff --git a/packages/browser/src/transports/fetch.ts b/packages/browser/src/transports/fetch.ts index 52ba6d71154c..f9a7c258d4ff 100644 --- a/packages/browser/src/transports/fetch.ts +++ b/packages/browser/src/transports/fetch.ts @@ -47,6 +47,7 @@ export function makeFetchTransport( } try { + // TODO: This may need a `suppresTracing` call in the future when we switch the browser SDK to OTEL return nativeFetch(options.url, requestOptions).then(response => { pendingBodySize -= requestSize; pendingCount--; diff --git a/packages/cloudflare/src/transport.ts b/packages/cloudflare/src/transport.ts index fd26b217c367..4f1314d693a7 100644 --- a/packages/cloudflare/src/transport.ts +++ b/packages/cloudflare/src/transport.ts @@ -1,4 +1,4 @@ -import { createTransport } from '@sentry/core'; +import { createTransport, suppressTracing } from '@sentry/core'; import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; import { SentryError } from '@sentry/utils'; @@ -89,14 +89,16 @@ export function makeCloudflareTransport(options: CloudflareTransportOptions): Tr ...options.fetchOptions, }; - return fetch(options.url, requestOptions).then(response => { - return { - statusCode: response.status, - headers: { - 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), - 'retry-after': response.headers.get('Retry-After'), - }, - }; + return suppressTracing(() => { + return fetch(options.url, requestOptions).then(response => { + return { + statusCode: response.status, + headers: { + 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), + 'retry-after': response.headers.get('Retry-After'), + }, + }; + }); }); } diff --git a/packages/deno/src/transports/index.ts b/packages/deno/src/transports/index.ts index c678688c2462..1b2b3c661af9 100644 --- a/packages/deno/src/transports/index.ts +++ b/packages/deno/src/transports/index.ts @@ -1,4 +1,4 @@ -import { createTransport } from '@sentry/core'; +import { createTransport, suppressTracing } from '@sentry/core'; import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; import { consoleSandbox, logger, rejectedSyncPromise } from '@sentry/utils'; @@ -37,14 +37,16 @@ export function makeFetchTransport(options: DenoTransportOptions): Transport { }; try { - return fetch(options.url, requestOptions).then(response => { - return { - statusCode: response.status, - headers: { - 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), - 'retry-after': response.headers.get('Retry-After'), - }, - }; + return suppressTracing(() => { + return fetch(options.url, requestOptions).then(response => { + return { + statusCode: response.status, + headers: { + 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), + 'retry-after': response.headers.get('Retry-After'), + }, + }; + }); }); } catch (e) { return rejectedSyncPromise(e); diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index 615506605c9b..0d5b2d4814d1 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -9,7 +9,6 @@ import { getCapturedScopesOnSpan, getCurrentScope, getIsolationScope, - isSentryRequestUrl, setCapturedScopesOnSpan, } from '@sentry/core'; import { getClient } from '@sentry/opentelemetry'; @@ -102,10 +101,6 @@ export const instrumentHttp = Object.assign( return false; } - if (isSentryRequestUrl(url, getClient())) { - return true; - } - const _ignoreOutgoingRequests = _httpOptions.ignoreOutgoingRequests; if (_ignoreOutgoingRequests && _ignoreOutgoingRequests(url, request)) { return true; diff --git a/packages/node/src/transports/http.ts b/packages/node/src/transports/http.ts index 751e4f3b3f4d..c4f13c89ee1b 100644 --- a/packages/node/src/transports/http.ts +++ b/packages/node/src/transports/http.ts @@ -79,11 +79,8 @@ export function makeNodeTransport(options: NodeTransportOptions): Transport { ? (new HttpsProxyAgent(proxy) as http.Agent) : new nativeHttpModule.Agent({ keepAlive, maxSockets: 30, timeout: 2000 }); - // This ensures we do not generate any spans in OpenTelemetry for the transport - return suppressTracing(() => { - const requestExecutor = createRequestExecutor(options, options.httpModule ?? nativeHttpModule, agent); - return createTransport(options, requestExecutor); - }); + const requestExecutor = createRequestExecutor(options, options.httpModule ?? nativeHttpModule, agent); + return createTransport(options, requestExecutor); } /** @@ -122,54 +119,59 @@ function createRequestExecutor( const { hostname, pathname, port, protocol, search } = new URL(options.url); return function makeRequest(request: TransportRequest): Promise { return new Promise((resolve, reject) => { - let body = streamFromBody(request.body); - - const headers: Record = { ...options.headers }; - - if (request.body.length > GZIP_THRESHOLD) { - headers['content-encoding'] = 'gzip'; - body = body.pipe(createGzip()); - } - - const req = httpModule.request( - { - method: 'POST', - agent, - headers, - hostname, - path: `${pathname}${search}`, - port, - protocol, - ca: options.caCerts, - }, - res => { - res.on('data', () => { - // Drain socket - }); - - res.on('end', () => { - // Drain socket - }); - - res.setEncoding('utf8'); - - // "Key-value pairs of header names and values. Header names are lower-cased." - // https://nodejs.org/api/http.html#http_message_headers - const retryAfterHeader = res.headers['retry-after'] ?? null; - const rateLimitsHeader = res.headers['x-sentry-rate-limits'] ?? null; - - resolve({ - statusCode: res.statusCode, - headers: { - 'retry-after': retryAfterHeader, - 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader) ? rateLimitsHeader[0] || null : rateLimitsHeader, - }, - }); - }, - ); - - req.on('error', reject); - body.pipe(req); + // This ensures we do not generate any spans in OpenTelemetry for the transport + suppressTracing(() => { + let body = streamFromBody(request.body); + + const headers: Record = { ...options.headers }; + + if (request.body.length > GZIP_THRESHOLD) { + headers['content-encoding'] = 'gzip'; + body = body.pipe(createGzip()); + } + + const req = httpModule.request( + { + method: 'POST', + agent, + headers, + hostname, + path: `${pathname}${search}`, + port, + protocol, + ca: options.caCerts, + }, + res => { + res.on('data', () => { + // Drain socket + }); + + res.on('end', () => { + // Drain socket + }); + + res.setEncoding('utf8'); + + // "Key-value pairs of header names and values. Header names are lower-cased." + // https://nodejs.org/api/http.html#http_message_headers + const retryAfterHeader = res.headers['retry-after'] ?? null; + const rateLimitsHeader = res.headers['x-sentry-rate-limits'] ?? null; + + resolve({ + statusCode: res.statusCode, + headers: { + 'retry-after': retryAfterHeader, + 'x-sentry-rate-limits': Array.isArray(rateLimitsHeader) + ? rateLimitsHeader[0] || null + : rateLimitsHeader, + }, + }); + }, + ); + + req.on('error', reject); + body.pipe(req); + }); }); }; } diff --git a/packages/vercel-edge/src/transports/index.ts b/packages/vercel-edge/src/transports/index.ts index 4e8a35ac7c39..b938647b4415 100644 --- a/packages/vercel-edge/src/transports/index.ts +++ b/packages/vercel-edge/src/transports/index.ts @@ -1,4 +1,4 @@ -import { createTransport } from '@sentry/core'; +import { createTransport, suppressTracing } from '@sentry/core'; import type { BaseTransportOptions, Transport, TransportMakeRequestResponse, TransportRequest } from '@sentry/types'; import { SentryError } from '@sentry/utils'; @@ -89,14 +89,16 @@ export function makeEdgeTransport(options: VercelEdgeTransportOptions): Transpor ...options.fetchOptions, }; - return fetch(options.url, requestOptions).then(response => { - return { - statusCode: response.status, - headers: { - 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), - 'retry-after': response.headers.get('Retry-After'), - }, - }; + return suppressTracing(() => { + return fetch(options.url, requestOptions).then(response => { + return { + statusCode: response.status, + headers: { + 'x-sentry-rate-limits': response.headers.get('X-Sentry-Rate-Limits'), + 'retry-after': response.headers.get('Retry-After'), + }, + }; + }); }); } From 5c1548560f41c2d88060c60f980d737f9bb0f97f Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 28 Aug 2024 14:36:30 +0200 Subject: [PATCH 23/64] test: Avoid race conditions with symlinks (#13498) Noticed here: https://github.com/getsentry/sentry-javascript/actions/runs/10594383971/job/29358133582 that this was sometimes failing. While looking into this, we actually did unnecessary work here - we had two levels of symlinks. Now we simply have a single symlink, and since we have unique dirs now we can skip checking for existing files etc. --- .../utils/generatePlugin.ts | 32 +++++++++---------- .../utils/staticAssets.ts | 20 +++--------- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/dev-packages/browser-integration-tests/utils/generatePlugin.ts b/dev-packages/browser-integration-tests/utils/generatePlugin.ts index 30939c40c955..9189ce63812f 100644 --- a/dev-packages/browser-integration-tests/utils/generatePlugin.ts +++ b/dev-packages/browser-integration-tests/utils/generatePlugin.ts @@ -4,7 +4,7 @@ import type { Package } from '@sentry/types'; import HtmlWebpackPlugin, { createHtmlTagObject } from 'html-webpack-plugin'; import type { Compiler } from 'webpack'; -import { addStaticAsset, addStaticAssetSymlink } from './staticAssets'; +import { addStaticAsset, symlinkAsset } from './staticAssets'; const LOADER_TEMPLATE = fs.readFileSync(path.join(__dirname, '../fixtures/loader.js'), 'utf-8'); const PACKAGES_DIR = path.join(__dirname, '..', '..', '..', 'packages'); @@ -214,7 +214,10 @@ class SentryScenarioGenerationPlugin { src: 'cdn.bundle.js', }); - addStaticAssetSymlink(this.localOutPath, path.resolve(PACKAGES_DIR, bundleName, bundlePath), 'cdn.bundle.js'); + symlinkAsset( + path.resolve(PACKAGES_DIR, bundleName, bundlePath), + path.join(this.localOutPath, 'cdn.bundle.js'), + ); if (useLoader) { const loaderConfig = LOADER_CONFIGS[bundleKey]; @@ -245,14 +248,13 @@ class SentryScenarioGenerationPlugin { const fileName = `${integration}.bundle.js`; // We add the files, but not a script tag - they are lazy-loaded - addStaticAssetSymlink( - this.localOutPath, + symlinkAsset( path.resolve( PACKAGES_DIR, 'feedback', BUNDLE_PATHS['feedback']?.[integrationBundleKey]?.replace('[INTEGRATION_NAME]', integration) || '', ), - fileName, + path.join(this.localOutPath, fileName), ); }); } @@ -262,26 +264,23 @@ class SentryScenarioGenerationPlugin { if (baseIntegrationFileName) { this.requiredIntegrations.forEach(integration => { const fileName = `${integration}.bundle.js`; - addStaticAssetSymlink( - this.localOutPath, + symlinkAsset( path.resolve( PACKAGES_DIR, 'browser', baseIntegrationFileName.replace('[INTEGRATION_NAME]', integration), ), - fileName, + path.join(this.localOutPath, fileName), ); if (integration === 'feedback') { - addStaticAssetSymlink( - this.localOutPath, + symlinkAsset( path.resolve(PACKAGES_DIR, 'feedback', 'build/bundles/feedback-modal.js'), - 'feedback-modal.bundle.js', + path.join(this.localOutPath, 'feedback-modal.bundle.js'), ); - addStaticAssetSymlink( - this.localOutPath, + symlinkAsset( path.resolve(PACKAGES_DIR, 'feedback', 'build/bundles/feedback-screenshot.js'), - 'feedback-screenshot.bundle.js', + path.join(this.localOutPath, 'feedback-screenshot.bundle.js'), ); } @@ -295,10 +294,9 @@ class SentryScenarioGenerationPlugin { const baseWasmFileName = BUNDLE_PATHS['wasm']?.[integrationBundleKey]; if (this.requiresWASMIntegration && baseWasmFileName) { - addStaticAssetSymlink( - this.localOutPath, + symlinkAsset( path.resolve(PACKAGES_DIR, 'wasm', baseWasmFileName), - 'wasm.bundle.js', + path.join(this.localOutPath, 'wasm.bundle.js'), ); const wasmObject = createHtmlTagObject('script', { diff --git a/dev-packages/browser-integration-tests/utils/staticAssets.ts b/dev-packages/browser-integration-tests/utils/staticAssets.ts index 447a3ad337f7..81c18eec1dcf 100644 --- a/dev-packages/browser-integration-tests/utils/staticAssets.ts +++ b/dev-packages/browser-integration-tests/utils/staticAssets.ts @@ -22,23 +22,11 @@ export function addStaticAsset(localOutPath: string, fileName: string, cb: () => symlinkAsset(newPath, path.join(localOutPath, fileName)); } -export function addStaticAssetSymlink(localOutPath: string, originalPath: string, fileName: string): void { - const newPath = path.join(STATIC_DIR, fileName); - - // Only copy files once - if (!fs.existsSync(newPath)) { - fs.symlinkSync(originalPath, newPath); - } - - symlinkAsset(newPath, path.join(localOutPath, fileName)); -} - -function symlinkAsset(originalPath: string, targetPath: string): void { +export function symlinkAsset(originalPath: string, targetPath: string): void { try { - fs.unlinkSync(targetPath); + fs.linkSync(originalPath, targetPath); } catch { - // ignore errors here + // ignore errors here, probably means the file already exists + // Since we always build into a new directory for each test, we can safely ignore this } - - fs.linkSync(originalPath, targetPath); } From 6479e88dca621a4e00502a98336ce1eefaef603f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 15:10:31 +0200 Subject: [PATCH 24/64] ref: Add external contributor to CHANGELOG.md (#13500) This PR adds the external contributor to the CHANGELOG.md file, so that they are credited for their contribution. See #13496 Co-authored-by: mydea <2411343+mydea@users.noreply.github.com> --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fa005af46c5..8232b230c17c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +Work in this release was contributed by @leopoldkristjansson. Thank you for your contribution! + ## 8.27.0 ### Important Changes From a428f85b99577f28c4a4ad8920e9f4e140d87743 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 28 Aug 2024 15:58:56 +0200 Subject: [PATCH 25/64] test(solidstart): Skip flaky test for now (#13505) This just skips a flaky E2E solidstart test. It is not a super critical test, so I'd skip this for now until we have time to investigate the flakiness - e.g. see https://github.com/getsentry/sentry-javascript/actions/runs/10596825068/job/29365892465 --- .../solidstart/tests/performance.client.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts index 52d9cb219401..2e5df36817ed 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/solidstart/tests/performance.client.test.ts @@ -46,7 +46,9 @@ test('sends a navigation transaction', async ({ page }) => { }); }); -test('updates the transaction when using the back button', async ({ page }) => { +// TODO: This test is flaky as of now, so disabling it. +// It often just times out on CI +test.skip('updates the transaction when using the back button', async ({ page }) => { // Solid Router sends a `-1` navigation when using the back button. // The sentry solidRouterBrowserTracingIntegration tries to update such // transactions with the proper name once the `useLocation` hook triggers. From b192678d5653b12ab5a011f8d9f31a5b6d93c1c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C5=A0tamcar?= Date: Wed, 28 Aug 2024 16:50:14 +0200 Subject: [PATCH 26/64] fix(vue): Correctly obtain component name (#13484) In some cases, Vue components do not have `options.name` defined, but instead have `options.__name`. Such components will be displayed as anonymous in Sentry and currently won't be matched in `trackComponents`. The same fix was also done in Vue devtools (vuejs/devtools#2020). In my case, the problem were components from my own project, but this change also fixes that. --- packages/vue/src/types.ts | 1 + packages/vue/src/vendor/components.ts | 2 +- packages/vue/test/vendor/components.test.ts | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/vue/src/types.ts b/packages/vue/src/types.ts index 0ed2f773ca2d..13d9e8588350 100644 --- a/packages/vue/src/types.ts +++ b/packages/vue/src/types.ts @@ -22,6 +22,7 @@ export type ViewModel = { propsData?: { [key: string]: any }; _componentTag?: string; __file?: string; + __name?: string; }; }; diff --git a/packages/vue/src/vendor/components.ts b/packages/vue/src/vendor/components.ts index a19dab78d99f..f5d05b5cbc6c 100644 --- a/packages/vue/src/vendor/components.ts +++ b/packages/vue/src/vendor/components.ts @@ -51,7 +51,7 @@ export const formatComponentName = (vm?: ViewModel, includeFile?: boolean): stri const options = vm.$options; - let name = options.name || options._componentTag; + let name = options.name || options._componentTag || options.__name; const file = options.__file; if (!name && file) { const match = file.match(/([^/\\]+)\.vue$/); diff --git a/packages/vue/test/vendor/components.test.ts b/packages/vue/test/vendor/components.test.ts index 49d184325ee0..5a210a0a2fb5 100644 --- a/packages/vue/test/vendor/components.test.ts +++ b/packages/vue/test/vendor/components.test.ts @@ -80,6 +80,19 @@ describe('formatComponentName', () => { }); }); + describe('when the options have a __name', () => { + it('returns the __name', () => { + // arrange + vm.$options.__name = 'my-component-name'; + + // act + const formattedName = formatComponentName(vm); + + // assert + expect(formattedName).toEqual(''); + }); + }); + describe('when the options have a __file', () => { describe('and we do not wish to include the filename', () => { it.each([ From 7e9a03803d761fdf661f305fdd58cf57eb2190c6 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 28 Aug 2024 17:28:20 +0200 Subject: [PATCH 27/64] feat(nestjs): Automatic instrumentation of nestjs interceptors after route execution (#13264) Adding a span 'Interceptor - After Span' to improve instrumentation for operations happening after the route execution is done. Tracking what happens in individual interceptors after the route is hard, so currently we create one span to trace everything that happens after the route is executed. --- .../nestjs-basic/src/app.controller.ts | 12 +- .../src/async-example.interceptor.ts | 17 + .../nestjs-basic/src/example-1.interceptor.ts | 15 + ...nterceptor.ts => example-2.interceptor.ts} | 4 +- .../nestjs-basic/tests/transactions.test.ts | 307 +++++++++++++++++- .../node-nestjs-basic/src/app.controller.ts | 12 +- .../src/async-example.interceptor.ts | 17 + .../src/example-1.interceptor.ts | 15 + .../src/example-2.interceptor.ts | 10 + .../src/example.interceptor.ts | 11 - .../tests/transactions.test.ts | 307 +++++++++++++++++- .../src/integrations/tracing/nest/helpers.ts | 29 +- .../nest/sentry-nest-instrumentation.ts | 96 ++++-- .../src/integrations/tracing/nest/types.ts | 10 +- 14 files changed, 794 insertions(+), 68 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-basic/src/async-example.interceptor.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-1.interceptor.ts rename dev-packages/e2e-tests/test-applications/nestjs-basic/src/{example.interceptor.ts => example-2.interceptor.ts} (65%) create mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/async-example.interceptor.ts create mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-1.interceptor.ts create mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-2.interceptor.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example.interceptor.ts diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts index 9cda3c96f9a6..1649cdb94b5f 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts @@ -1,11 +1,13 @@ import { Controller, Get, Param, ParseIntPipe, UseFilters, UseGuards, UseInterceptors } from '@nestjs/common'; import { flush } from '@sentry/nestjs'; import { AppService } from './app.service'; +import { AsyncInterceptor } from './async-example.interceptor'; +import { ExampleInterceptor1 } from './example-1.interceptor'; +import { ExampleInterceptor2 } from './example-2.interceptor'; import { ExampleExceptionGlobalFilter } from './example-global-filter.exception'; import { ExampleExceptionLocalFilter } from './example-local-filter.exception'; import { ExampleLocalFilter } from './example-local.filter'; import { ExampleGuard } from './example.guard'; -import { ExampleInterceptor } from './example.interceptor'; @Controller() @UseFilters(ExampleLocalFilter) @@ -29,11 +31,17 @@ export class AppController { } @Get('test-interceptor-instrumentation') - @UseInterceptors(ExampleInterceptor) + @UseInterceptors(ExampleInterceptor1, ExampleInterceptor2) testInterceptorInstrumentation() { return this.appService.testSpan(); } + @Get('test-async-interceptor-instrumentation') + @UseInterceptors(AsyncInterceptor) + testAsyncInterceptorInstrumentation() { + return this.appService.testSpan(); + } + @Get('test-pipe-instrumentation/:id') testPipeInstrumentation(@Param('id', ParseIntPipe) id: number) { return { value: id }; diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/async-example.interceptor.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/async-example.interceptor.ts new file mode 100644 index 000000000000..ac0ee60acc51 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/async-example.interceptor.ts @@ -0,0 +1,17 @@ +import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; +import * as Sentry from '@sentry/nestjs'; +import { tap } from 'rxjs'; + +@Injectable() +export class AsyncInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler) { + Sentry.startSpan({ name: 'test-async-interceptor-span' }, () => {}); + return Promise.resolve( + next.handle().pipe( + tap(() => { + Sentry.startSpan({ name: 'test-async-interceptor-span-after-route' }, () => {}); + }), + ), + ); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-1.interceptor.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-1.interceptor.ts new file mode 100644 index 000000000000..81c9f70d30e2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-1.interceptor.ts @@ -0,0 +1,15 @@ +import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; +import * as Sentry from '@sentry/nestjs'; +import { tap } from 'rxjs'; + +@Injectable() +export class ExampleInterceptor1 implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler) { + Sentry.startSpan({ name: 'test-interceptor-span-1' }, () => {}); + return next.handle().pipe( + tap(() => { + Sentry.startSpan({ name: 'test-interceptor-span-after-route' }, () => {}); + }), + ); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example.interceptor.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-2.interceptor.ts similarity index 65% rename from dev-packages/e2e-tests/test-applications/nestjs-basic/src/example.interceptor.ts rename to dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-2.interceptor.ts index 75c301b4cffc..2cf9dfb9e043 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example.interceptor.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/example-2.interceptor.ts @@ -2,9 +2,9 @@ import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nes import * as Sentry from '@sentry/nestjs'; @Injectable() -export class ExampleInterceptor implements NestInterceptor { +export class ExampleInterceptor2 implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler) { - Sentry.startSpan({ name: 'test-interceptor-span' }, () => {}); + Sentry.startSpan({ name: 'test-interceptor-span-2' }, () => {}); return next.handle().pipe(); } } diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts index 555b6357ade8..e33e63f319ca 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts @@ -341,7 +341,7 @@ test('API route transaction includes nest pipe span for invalid request', async ); }); -test('API route transaction includes nest interceptor span. Spans created in and after interceptor are nested correctly', async ({ +test('API route transaction includes nest interceptor spans before route execution. Spans created in and after interceptor are nested correctly', async ({ baseURL, }) => { const pageloadTransactionEventPromise = waitForTransaction('nestjs-basic', transactionEvent => { @@ -356,6 +356,7 @@ test('API route transaction includes nest interceptor span. Spans created in and const transactionEvent = await pageloadTransactionEventPromise; + // check if interceptor spans before route execution exist expect(transactionEvent).toEqual( expect.objectContaining({ spans: expect.arrayContaining([ @@ -366,7 +367,22 @@ test('API route transaction includes nest interceptor span. Spans created in and 'sentry.op': 'middleware.nestjs', 'sentry.origin': 'auto.middleware.nestjs', }, - description: 'ExampleInterceptor', + description: 'ExampleInterceptor1', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + op: 'middleware.nestjs', + origin: 'auto.middleware.nestjs', + }, + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + 'sentry.op': 'middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs', + }, + description: 'ExampleInterceptor2', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -378,9 +394,13 @@ test('API route transaction includes nest interceptor span. Spans created in and }), ); - const exampleInterceptorSpan = transactionEvent.spans.find(span => span.description === 'ExampleInterceptor'); - const exampleInterceptorSpanId = exampleInterceptorSpan?.span_id; + // get interceptor spans + const exampleInterceptor1Span = transactionEvent.spans.find(span => span.description === 'ExampleInterceptor1'); + const exampleInterceptor1SpanId = exampleInterceptor1Span?.span_id; + const exampleInterceptor2Span = transactionEvent.spans.find(span => span.description === 'ExampleInterceptor2'); + const exampleInterceptor2SpanId = exampleInterceptor2Span?.span_id; + // check if manually started spans exist expect(transactionEvent).toEqual( expect.objectContaining({ spans: expect.arrayContaining([ @@ -399,7 +419,105 @@ test('API route transaction includes nest interceptor span. Spans created in and span_id: expect.any(String), trace_id: expect.any(String), data: expect.any(Object), - description: 'test-interceptor-span', + description: 'test-interceptor-span-1', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + origin: 'manual', + }, + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: expect.any(Object), + description: 'test-interceptor-span-2', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + origin: 'manual', + }, + ]), + }), + ); + + // verify correct span parent-child relationships + const testInterceptor1Span = transactionEvent.spans.find(span => span.description === 'test-interceptor-span-1'); + const testInterceptor2Span = transactionEvent.spans.find(span => span.description === 'test-interceptor-span-2'); + const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); + + // 'ExampleInterceptor1' is the parent of 'test-interceptor-span-1' + expect(testInterceptor1Span.parent_span_id).toBe(exampleInterceptor1SpanId); + + // 'ExampleInterceptor1' is NOT the parent of 'test-controller-span' + expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptor1SpanId); + + // 'ExampleInterceptor2' is the parent of 'test-interceptor-span-2' + expect(testInterceptor2Span.parent_span_id).toBe(exampleInterceptor2SpanId); + + // 'ExampleInterceptor2' is NOT the parent of 'test-controller-span' + expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptor2SpanId); +}); + +test('API route transaction includes exactly one nest interceptor span after route execution. Spans created in controller and in interceptor are nested correctly', async ({ + baseURL, +}) => { + const pageloadTransactionEventPromise = waitForTransaction('nestjs-basic', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'http.server' && + transactionEvent?.transaction === 'GET /test-interceptor-instrumentation' + ); + }); + + const response = await fetch(`${baseURL}/test-interceptor-instrumentation`); + expect(response.status).toBe(200); + + const transactionEvent = await pageloadTransactionEventPromise; + + // check if interceptor spans after route execution exist + expect(transactionEvent).toEqual( + expect.objectContaining({ + spans: expect.arrayContaining([ + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + 'sentry.op': 'middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs', + }, + description: 'Interceptors - After Route', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + op: 'middleware.nestjs', + origin: 'auto.middleware.nestjs', + }, + ]), + }), + ); + + // check that exactly one after route span is sent + const allInterceptorSpansAfterRoute = transactionEvent.spans.filter( + span => span.description === 'Interceptors - After Route', + ); + expect(allInterceptorSpansAfterRoute.length).toBe(1); + + // get interceptor span + const exampleInterceptorSpanAfterRoute = transactionEvent.spans.find( + span => span.description === 'Interceptors - After Route', + ); + const exampleInterceptorSpanAfterRouteId = exampleInterceptorSpanAfterRoute?.span_id; + + // check if manually started span in interceptor after route exists + expect(transactionEvent).toEqual( + expect.objectContaining({ + spans: expect.arrayContaining([ + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: expect.any(Object), + description: 'test-interceptor-span-after-route', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -411,12 +529,181 @@ test('API route transaction includes nest interceptor span. Spans created in and ); // verify correct span parent-child relationships - const testInterceptorSpan = transactionEvent.spans.find(span => span.description === 'test-interceptor-span'); + const testInterceptorSpanAfterRoute = transactionEvent.spans.find( + span => span.description === 'test-interceptor-span-after-route', + ); + const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); + + // 'Interceptor - After Route' is the parent of 'test-interceptor-span-after-route' + expect(testInterceptorSpanAfterRoute.parent_span_id).toBe(exampleInterceptorSpanAfterRouteId); + + // 'Interceptor - After Route' is NOT the parent of 'test-controller-span' + expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptorSpanAfterRouteId); +}); + +test('API route transaction includes nest async interceptor spans before route execution. Spans created in and after async interceptor are nested correctly', async ({ + baseURL, +}) => { + const pageloadTransactionEventPromise = waitForTransaction('nestjs-basic', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'http.server' && + transactionEvent?.transaction === 'GET /test-async-interceptor-instrumentation' + ); + }); + + const response = await fetch(`${baseURL}/test-async-interceptor-instrumentation`); + expect(response.status).toBe(200); + + const transactionEvent = await pageloadTransactionEventPromise; + + // check if interceptor spans before route execution exist + expect(transactionEvent).toEqual( + expect.objectContaining({ + spans: expect.arrayContaining([ + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + 'sentry.op': 'middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs', + }, + description: 'AsyncInterceptor', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + op: 'middleware.nestjs', + origin: 'auto.middleware.nestjs', + }, + ]), + }), + ); + + // get interceptor spans + const exampleAsyncInterceptor = transactionEvent.spans.find(span => span.description === 'AsyncInterceptor'); + const exampleAsyncInterceptorSpanId = exampleAsyncInterceptor?.span_id; + + // check if manually started spans exist + expect(transactionEvent).toEqual( + expect.objectContaining({ + spans: expect.arrayContaining([ + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: expect.any(Object), + description: 'test-controller-span', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + origin: 'manual', + }, + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: expect.any(Object), + description: 'test-async-interceptor-span', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + origin: 'manual', + }, + ]), + }), + ); + + // verify correct span parent-child relationships + const testAsyncInterceptorSpan = transactionEvent.spans.find( + span => span.description === 'test-async-interceptor-span', + ); + const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); + + // 'AsyncInterceptor' is the parent of 'test-async-interceptor-span' + expect(testAsyncInterceptorSpan.parent_span_id).toBe(exampleAsyncInterceptorSpanId); + + // 'AsyncInterceptor' is NOT the parent of 'test-controller-span' + expect(testControllerSpan.parent_span_id).not.toBe(exampleAsyncInterceptorSpanId); +}); + +test('API route transaction includes exactly one nest async interceptor span after route execution. Spans created in controller and in async interceptor are nested correctly', async ({ + baseURL, +}) => { + const pageloadTransactionEventPromise = waitForTransaction('nestjs-basic', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'http.server' && + transactionEvent?.transaction === 'GET /test-async-interceptor-instrumentation' + ); + }); + + const response = await fetch(`${baseURL}/test-async-interceptor-instrumentation`); + expect(response.status).toBe(200); + + const transactionEvent = await pageloadTransactionEventPromise; + + // check if interceptor spans after route execution exist + expect(transactionEvent).toEqual( + expect.objectContaining({ + spans: expect.arrayContaining([ + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + 'sentry.op': 'middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs', + }, + description: 'Interceptors - After Route', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + op: 'middleware.nestjs', + origin: 'auto.middleware.nestjs', + }, + ]), + }), + ); + + // check that exactly one after route span is sent + const allInterceptorSpansAfterRoute = transactionEvent.spans.filter( + span => span.description === 'Interceptors - After Route', + ); + expect(allInterceptorSpansAfterRoute.length).toBe(1); + + // get interceptor span + const exampleInterceptorSpanAfterRoute = transactionEvent.spans.find( + span => span.description === 'Interceptors - After Route', + ); + const exampleInterceptorSpanAfterRouteId = exampleInterceptorSpanAfterRoute?.span_id; + + // check if manually started span in interceptor after route exists + expect(transactionEvent).toEqual( + expect.objectContaining({ + spans: expect.arrayContaining([ + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: expect.any(Object), + description: 'test-async-interceptor-span-after-route', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + origin: 'manual', + }, + ]), + }), + ); + + // verify correct span parent-child relationships + const testInterceptorSpanAfterRoute = transactionEvent.spans.find( + span => span.description === 'test-async-interceptor-span-after-route', + ); const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); - // 'ExampleInterceptor' is the parent of 'test-interceptor-span' - expect(testInterceptorSpan.parent_span_id).toBe(exampleInterceptorSpanId); + // 'Interceptor - After Route' is the parent of 'test-interceptor-span-after-route' + expect(testInterceptorSpanAfterRoute.parent_span_id).toBe(exampleInterceptorSpanAfterRouteId); - // 'ExampleInterceptor' is NOT the parent of 'test-controller-span' - expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptorSpanId); + // 'Interceptor - After Route' is NOT the parent of 'test-controller-span' + expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptorSpanAfterRouteId); }); diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.controller.ts index ec0a921da2c4..1f141dc0f966 100644 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.controller.ts +++ b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.controller.ts @@ -1,8 +1,10 @@ import { Controller, Get, Param, ParseIntPipe, UseGuards, UseInterceptors } from '@nestjs/common'; import { flush } from '@sentry/nestjs'; import { AppService } from './app.service'; +import { AsyncInterceptor } from './async-example.interceptor'; +import { ExampleInterceptor1 } from './example-1.interceptor'; +import { ExampleInterceptor2 } from './example-2.interceptor'; import { ExampleGuard } from './example.guard'; -import { ExampleInterceptor } from './example.interceptor'; @Controller() export class AppController { @@ -25,11 +27,17 @@ export class AppController { } @Get('test-interceptor-instrumentation') - @UseInterceptors(ExampleInterceptor) + @UseInterceptors(ExampleInterceptor1, ExampleInterceptor2) testInterceptorInstrumentation() { return this.appService.testSpan(); } + @Get('test-async-interceptor-instrumentation') + @UseInterceptors(AsyncInterceptor) + testAsyncInterceptorInstrumentation() { + return this.appService.testSpan(); + } + @Get('test-pipe-instrumentation/:id') testPipeInstrumentation(@Param('id', ParseIntPipe) id: number) { return { value: id }; diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/async-example.interceptor.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/async-example.interceptor.ts new file mode 100644 index 000000000000..ac0ee60acc51 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/async-example.interceptor.ts @@ -0,0 +1,17 @@ +import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; +import * as Sentry from '@sentry/nestjs'; +import { tap } from 'rxjs'; + +@Injectable() +export class AsyncInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler) { + Sentry.startSpan({ name: 'test-async-interceptor-span' }, () => {}); + return Promise.resolve( + next.handle().pipe( + tap(() => { + Sentry.startSpan({ name: 'test-async-interceptor-span-after-route' }, () => {}); + }), + ), + ); + } +} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-1.interceptor.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-1.interceptor.ts new file mode 100644 index 000000000000..81c9f70d30e2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-1.interceptor.ts @@ -0,0 +1,15 @@ +import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; +import * as Sentry from '@sentry/nestjs'; +import { tap } from 'rxjs'; + +@Injectable() +export class ExampleInterceptor1 implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler) { + Sentry.startSpan({ name: 'test-interceptor-span-1' }, () => {}); + return next.handle().pipe( + tap(() => { + Sentry.startSpan({ name: 'test-interceptor-span-after-route' }, () => {}); + }), + ); + } +} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-2.interceptor.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-2.interceptor.ts new file mode 100644 index 000000000000..2cf9dfb9e043 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-2.interceptor.ts @@ -0,0 +1,10 @@ +import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; +import * as Sentry from '@sentry/nestjs'; + +@Injectable() +export class ExampleInterceptor2 implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler) { + Sentry.startSpan({ name: 'test-interceptor-span-2' }, () => {}); + return next.handle().pipe(); + } +} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example.interceptor.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example.interceptor.ts deleted file mode 100644 index 260c1798449f..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example.interceptor.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; -import * as Sentry from '@sentry/nestjs'; -import { Observable } from 'rxjs'; - -@Injectable() -export class ExampleInterceptor implements NestInterceptor { - intercept(context: ExecutionContext, next: CallHandler): Observable { - Sentry.startSpan({ name: 'test-interceptor-span' }, () => {}); - return next.handle().pipe(); - } -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/transactions.test.ts index cb04bc06839e..eb52dfe98585 100644 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/transactions.test.ts @@ -339,7 +339,7 @@ test('API route transaction includes nest pipe span for invalid request', async ); }); -test('API route transaction includes nest interceptor span. Spans created in and after interceptor are nested correctly', async ({ +test('API route transaction includes nest interceptor spans before route execution. Spans created in and after interceptor are nested correctly', async ({ baseURL, }) => { const pageloadTransactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { @@ -354,6 +354,7 @@ test('API route transaction includes nest interceptor span. Spans created in and const transactionEvent = await pageloadTransactionEventPromise; + // check if interceptor spans before route execution exist expect(transactionEvent).toEqual( expect.objectContaining({ spans: expect.arrayContaining([ @@ -364,7 +365,22 @@ test('API route transaction includes nest interceptor span. Spans created in and 'sentry.op': 'middleware.nestjs', 'sentry.origin': 'auto.middleware.nestjs', }, - description: 'ExampleInterceptor', + description: 'ExampleInterceptor1', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + op: 'middleware.nestjs', + origin: 'auto.middleware.nestjs', + }, + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + 'sentry.op': 'middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs', + }, + description: 'ExampleInterceptor2', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -376,9 +392,13 @@ test('API route transaction includes nest interceptor span. Spans created in and }), ); - const exampleInterceptorSpan = transactionEvent.spans.find(span => span.description === 'ExampleInterceptor'); - const exampleInterceptorSpanId = exampleInterceptorSpan?.span_id; + // get interceptor spans + const exampleInterceptor1Span = transactionEvent.spans.find(span => span.description === 'ExampleInterceptor1'); + const exampleInterceptor1SpanId = exampleInterceptor1Span?.span_id; + const exampleInterceptor2Span = transactionEvent.spans.find(span => span.description === 'ExampleInterceptor2'); + const exampleInterceptor2SpanId = exampleInterceptor2Span?.span_id; + // check if manually started spans exist expect(transactionEvent).toEqual( expect.objectContaining({ spans: expect.arrayContaining([ @@ -397,7 +417,105 @@ test('API route transaction includes nest interceptor span. Spans created in and span_id: expect.any(String), trace_id: expect.any(String), data: expect.any(Object), - description: 'test-interceptor-span', + description: 'test-interceptor-span-1', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + origin: 'manual', + }, + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: expect.any(Object), + description: 'test-interceptor-span-2', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + origin: 'manual', + }, + ]), + }), + ); + + // verify correct span parent-child relationships + const testInterceptor1Span = transactionEvent.spans.find(span => span.description === 'test-interceptor-span-1'); + const testInterceptor2Span = transactionEvent.spans.find(span => span.description === 'test-interceptor-span-2'); + const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); + + // 'ExampleInterceptor1' is the parent of 'test-interceptor-span-1' + expect(testInterceptor1Span.parent_span_id).toBe(exampleInterceptor1SpanId); + + // 'ExampleInterceptor1' is NOT the parent of 'test-controller-span' + expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptor1SpanId); + + // 'ExampleInterceptor2' is the parent of 'test-interceptor-span-2' + expect(testInterceptor2Span.parent_span_id).toBe(exampleInterceptor2SpanId); + + // 'ExampleInterceptor2' is NOT the parent of 'test-controller-span' + expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptor2SpanId); +}); + +test('API route transaction includes exactly one nest interceptor span after route execution. Spans created in controller and in interceptor are nested correctly', async ({ + baseURL, +}) => { + const pageloadTransactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'http.server' && + transactionEvent?.transaction === 'GET /test-interceptor-instrumentation' + ); + }); + + const response = await fetch(`${baseURL}/test-interceptor-instrumentation`); + expect(response.status).toBe(200); + + const transactionEvent = await pageloadTransactionEventPromise; + + // check if interceptor spans after route execution exist + expect(transactionEvent).toEqual( + expect.objectContaining({ + spans: expect.arrayContaining([ + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + 'sentry.op': 'middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs', + }, + description: 'Interceptors - After Route', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + op: 'middleware.nestjs', + origin: 'auto.middleware.nestjs', + }, + ]), + }), + ); + + // check that exactly one after route span is sent + const allInterceptorSpansAfterRoute = transactionEvent.spans.filter( + span => span.description === 'Interceptors - After Route', + ); + expect(allInterceptorSpansAfterRoute.length).toBe(1); + + // get interceptor span + const exampleInterceptorSpanAfterRoute = transactionEvent.spans.find( + span => span.description === 'Interceptors - After Route', + ); + const exampleInterceptorSpanAfterRouteId = exampleInterceptorSpanAfterRoute?.span_id; + + // check if manually started span in interceptor after route exists + expect(transactionEvent).toEqual( + expect.objectContaining({ + spans: expect.arrayContaining([ + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: expect.any(Object), + description: 'test-interceptor-span-after-route', parent_span_id: expect.any(String), start_timestamp: expect.any(Number), timestamp: expect.any(Number), @@ -409,12 +527,181 @@ test('API route transaction includes nest interceptor span. Spans created in and ); // verify correct span parent-child relationships - const testInterceptorSpan = transactionEvent.spans.find(span => span.description === 'test-interceptor-span'); + const testInterceptorSpanAfterRoute = transactionEvent.spans.find( + span => span.description === 'test-interceptor-span-after-route', + ); + const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); + + // 'Interceptor - After Route' is the parent of 'test-interceptor-span-after-route' + expect(testInterceptorSpanAfterRoute.parent_span_id).toBe(exampleInterceptorSpanAfterRouteId); + + // 'Interceptor - After Route' is NOT the parent of 'test-controller-span' + expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptorSpanAfterRouteId); +}); + +test('API route transaction includes nest async interceptor spans before route execution. Spans created in and after async interceptor are nested correctly', async ({ + baseURL, +}) => { + const pageloadTransactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'http.server' && + transactionEvent?.transaction === 'GET /test-async-interceptor-instrumentation' + ); + }); + + const response = await fetch(`${baseURL}/test-async-interceptor-instrumentation`); + expect(response.status).toBe(200); + + const transactionEvent = await pageloadTransactionEventPromise; + + // check if interceptor spans before route execution exist + expect(transactionEvent).toEqual( + expect.objectContaining({ + spans: expect.arrayContaining([ + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + 'sentry.op': 'middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs', + }, + description: 'AsyncInterceptor', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + op: 'middleware.nestjs', + origin: 'auto.middleware.nestjs', + }, + ]), + }), + ); + + // get interceptor spans + const exampleAsyncInterceptor = transactionEvent.spans.find(span => span.description === 'AsyncInterceptor'); + const exampleAsyncInterceptorSpanId = exampleAsyncInterceptor?.span_id; + + // check if manually started spans exist + expect(transactionEvent).toEqual( + expect.objectContaining({ + spans: expect.arrayContaining([ + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: expect.any(Object), + description: 'test-controller-span', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + origin: 'manual', + }, + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: expect.any(Object), + description: 'test-async-interceptor-span', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + origin: 'manual', + }, + ]), + }), + ); + + // verify correct span parent-child relationships + const testAsyncInterceptorSpan = transactionEvent.spans.find( + span => span.description === 'test-async-interceptor-span', + ); + const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); + + // 'AsyncInterceptor' is the parent of 'test-async-interceptor-span' + expect(testAsyncInterceptorSpan.parent_span_id).toBe(exampleAsyncInterceptorSpanId); + + // 'AsyncInterceptor' is NOT the parent of 'test-controller-span' + expect(testControllerSpan.parent_span_id).not.toBe(exampleAsyncInterceptorSpanId); +}); + +test('API route transaction includes exactly one nest async interceptor span after route execution. Spans created in controller and in async interceptor are nested correctly', async ({ + baseURL, +}) => { + const pageloadTransactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { + return ( + transactionEvent?.contexts?.trace?.op === 'http.server' && + transactionEvent?.transaction === 'GET /test-async-interceptor-instrumentation' + ); + }); + + const response = await fetch(`${baseURL}/test-async-interceptor-instrumentation`); + expect(response.status).toBe(200); + + const transactionEvent = await pageloadTransactionEventPromise; + + // check if interceptor spans after route execution exist + expect(transactionEvent).toEqual( + expect.objectContaining({ + spans: expect.arrayContaining([ + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + 'sentry.op': 'middleware.nestjs', + 'sentry.origin': 'auto.middleware.nestjs', + }, + description: 'Interceptors - After Route', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + op: 'middleware.nestjs', + origin: 'auto.middleware.nestjs', + }, + ]), + }), + ); + + // check that exactly one after route span is sent + const allInterceptorSpansAfterRoute = transactionEvent.spans.filter( + span => span.description === 'Interceptors - After Route', + ); + expect(allInterceptorSpansAfterRoute.length).toBe(1); + + // get interceptor span + const exampleInterceptorSpanAfterRoute = transactionEvent.spans.find( + span => span.description === 'Interceptors - After Route', + ); + const exampleInterceptorSpanAfterRouteId = exampleInterceptorSpanAfterRoute?.span_id; + + // check if manually started span in interceptor after route exists + expect(transactionEvent).toEqual( + expect.objectContaining({ + spans: expect.arrayContaining([ + { + span_id: expect.any(String), + trace_id: expect.any(String), + data: expect.any(Object), + description: 'test-async-interceptor-span-after-route', + parent_span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + status: 'ok', + origin: 'manual', + }, + ]), + }), + ); + + // verify correct span parent-child relationships + const testInterceptorSpanAfterRoute = transactionEvent.spans.find( + span => span.description === 'test-async-interceptor-span-after-route', + ); const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); - // 'ExampleInterceptor' is the parent of 'test-interceptor-span' - expect(testInterceptorSpan.parent_span_id).toBe(exampleInterceptorSpanId); + // 'Interceptor - After Route' is the parent of 'test-interceptor-span-after-route' + expect(testInterceptorSpanAfterRoute.parent_span_id).toBe(exampleInterceptorSpanAfterRouteId); - // 'ExampleInterceptor' is NOT the parent of 'test-controller-span' - expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptorSpanId); + // 'Interceptor - After Route' is NOT the parent of 'test-controller-span' + expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptorSpanAfterRouteId); }); diff --git a/packages/node/src/integrations/tracing/nest/helpers.ts b/packages/node/src/integrations/tracing/nest/helpers.ts index babf80022c1f..e2d3c4c573b9 100644 --- a/packages/node/src/integrations/tracing/nest/helpers.ts +++ b/packages/node/src/integrations/tracing/nest/helpers.ts @@ -1,6 +1,7 @@ -import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; +import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, withActiveSpan } from '@sentry/core'; +import type { Span } from '@sentry/types'; import { addNonEnumerableProperty } from '@sentry/utils'; -import type { CatchTarget, InjectableTarget } from './types'; +import type { CatchTarget, InjectableTarget, Observable, Subscription } from './types'; const sentryPatched = 'sentryPatched'; @@ -23,12 +24,32 @@ export function isPatched(target: InjectableTarget | CatchTarget): boolean { * Returns span options for nest middleware spans. */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function getMiddlewareSpanOptions(target: InjectableTarget | CatchTarget) { +export function getMiddlewareSpanOptions(target: InjectableTarget | CatchTarget, name: string | undefined = undefined) { + const span_name = name ?? target.name; // fallback to class name if no name is provided + return { - name: target.name, + name: span_name, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'middleware.nestjs', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.middleware.nestjs', }, }; } + +/** + * Adds instrumentation to a js observable and attaches the span to an active parent span. + */ +export function instrumentObservable(observable: Observable, activeSpan: Span | undefined): void { + if (activeSpan) { + // eslint-disable-next-line @typescript-eslint/unbound-method + observable.subscribe = new Proxy(observable.subscribe, { + apply: (originalSubscribe, thisArgSubscribe, argsSubscribe) => { + return withActiveSpan(activeSpan, () => { + const subscription: Subscription = originalSubscribe.apply(thisArgSubscribe, argsSubscribe); + subscription.add(() => activeSpan.end()); + return subscription; + }); + }, + }); + } +} diff --git a/packages/node/src/integrations/tracing/nest/sentry-nest-instrumentation.ts b/packages/node/src/integrations/tracing/nest/sentry-nest-instrumentation.ts index a8d02e5cbe69..b8ea782ee66f 100644 --- a/packages/node/src/integrations/tracing/nest/sentry-nest-instrumentation.ts +++ b/packages/node/src/integrations/tracing/nest/sentry-nest-instrumentation.ts @@ -5,11 +5,11 @@ import { InstrumentationNodeModuleDefinition, InstrumentationNodeModuleFile, } from '@opentelemetry/instrumentation'; -import { getActiveSpan, startSpan, startSpanManual, withActiveSpan } from '@sentry/core'; +import { getActiveSpan, startInactiveSpan, startSpan, startSpanManual, withActiveSpan } from '@sentry/core'; import type { Span } from '@sentry/types'; -import { SDK_VERSION } from '@sentry/utils'; -import { getMiddlewareSpanOptions, isPatched } from './helpers'; -import type { CatchTarget, InjectableTarget } from './types'; +import { SDK_VERSION, addNonEnumerableProperty, isThenable } from '@sentry/utils'; +import { getMiddlewareSpanOptions, instrumentObservable, isPatched } from './helpers'; +import type { CallHandler, CatchTarget, InjectableTarget, MinimalNestJsExecutionContext, Observable } from './types'; const supportedVersions = ['>=8.0.0 <11']; @@ -163,33 +163,79 @@ export class SentryNestInstrumentation extends InstrumentationBase { target.prototype.intercept = new Proxy(target.prototype.intercept, { apply: (originalIntercept, thisArgIntercept, argsIntercept) => { - const [executionContext, next, args] = argsIntercept; - const prevSpan = getActiveSpan(); + const context: MinimalNestJsExecutionContext = argsIntercept[0]; + const next: CallHandler = argsIntercept[1]; - return startSpanManual(getMiddlewareSpanOptions(target), (span: Span) => { - const nextProxy = new Proxy(next, { - get: (thisArgNext, property, receiver) => { - if (property === 'handle') { - const originalHandle = Reflect.get(thisArgNext, property, receiver); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (...args: any[]) => { - span.end(); - - if (prevSpan) { - return withActiveSpan(prevSpan, () => { - return Reflect.apply(originalHandle, thisArgNext, args); - }); - } else { - return Reflect.apply(originalHandle, thisArgNext, args); + const parentSpan = getActiveSpan(); + let afterSpan: Span; + + return startSpanManual(getMiddlewareSpanOptions(target), (beforeSpan: Span) => { + // eslint-disable-next-line @typescript-eslint/unbound-method + next.handle = new Proxy(next.handle, { + apply: (originalHandle, thisArgHandle, argsHandle) => { + beforeSpan.end(); + + if (parentSpan) { + return withActiveSpan(parentSpan, () => { + const handleReturnObservable = Reflect.apply(originalHandle, thisArgHandle, argsHandle); + + if (!context._sentryInterceptorInstrumented) { + addNonEnumerableProperty(context, '_sentryInterceptorInstrumented', true); + afterSpan = startInactiveSpan( + getMiddlewareSpanOptions(target, 'Interceptors - After Route'), + ); } - }; - } - return Reflect.get(target, property, receiver); + return handleReturnObservable; + }); + } else { + const handleReturnObservable = Reflect.apply(originalHandle, thisArgHandle, argsHandle); + + if (!context._sentryInterceptorInstrumented) { + addNonEnumerableProperty(context, '_sentryInterceptorInstrumented', true); + afterSpan = startInactiveSpan(getMiddlewareSpanOptions(target, 'Interceptors - After Route')); + } + + return handleReturnObservable; + } }, }); - return originalIntercept.apply(thisArgIntercept, [executionContext, nextProxy, args]); + let returnedObservableInterceptMaybePromise: Observable | Promise>; + + try { + returnedObservableInterceptMaybePromise = originalIntercept.apply(thisArgIntercept, argsIntercept); + } catch (e) { + beforeSpan?.end(); + afterSpan?.end(); + throw e; + } + + if (!afterSpan) { + return returnedObservableInterceptMaybePromise; + } + + // handle async interceptor + if (isThenable(returnedObservableInterceptMaybePromise)) { + return returnedObservableInterceptMaybePromise.then( + observable => { + instrumentObservable(observable, afterSpan ?? parentSpan); + return observable; + }, + e => { + beforeSpan?.end(); + afterSpan?.end(); + throw e; + }, + ); + } + + // handle sync interceptor + if (typeof returnedObservableInterceptMaybePromise.subscribe === 'function') { + instrumentObservable(returnedObservableInterceptMaybePromise, afterSpan ?? parentSpan); + } + + return returnedObservableInterceptMaybePromise; }); }, }); diff --git a/packages/node/src/integrations/tracing/nest/types.ts b/packages/node/src/integrations/tracing/nest/types.ts index 42aa0b003315..18bd26b31ccb 100644 --- a/packages/node/src/integrations/tracing/nest/types.ts +++ b/packages/node/src/integrations/tracing/nest/types.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -interface MinimalNestJsExecutionContext { +export interface MinimalNestJsExecutionContext { getType: () => string; switchToHttp: () => { @@ -14,6 +14,8 @@ interface MinimalNestJsExecutionContext { method?: string; }; }; + + _sentryInterceptorInstrumented?: boolean; } export interface NestJsErrorFilter { @@ -27,11 +29,15 @@ export interface MinimalNestJsApp { }) => void; } +export interface Subscription { + add(...args: any[]): void; +} + /** * A minimal interface for an Observable. */ export interface Observable { - subscribe(observer: (value: T) => void): void; + subscribe(next?: (value: T) => void, error?: (err: any) => void, complete?: () => void): Subscription; } /** From 8d9fe350c68c55901f743cfcbcf1ecaf1dca8402 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 28 Aug 2024 17:28:58 +0200 Subject: [PATCH 28/64] ref(nestjs): Move decorators to folder (#13509) Just moving the decorators to improve the structure of the nest SDK package a bit. --- .../src/{cron-decorator.ts => decorators/sentry-cron.ts} | 0 .../src/{span-decorator.ts => decorators/sentry-traced.ts} | 0 .../src/{error-decorator.ts => decorators/with-sentry.ts} | 2 +- packages/nestjs/src/index.ts | 6 +++--- 4 files changed, 4 insertions(+), 4 deletions(-) rename packages/nestjs/src/{cron-decorator.ts => decorators/sentry-cron.ts} (100%) rename packages/nestjs/src/{span-decorator.ts => decorators/sentry-traced.ts} (100%) rename packages/nestjs/src/{error-decorator.ts => decorators/with-sentry.ts} (94%) diff --git a/packages/nestjs/src/cron-decorator.ts b/packages/nestjs/src/decorators/sentry-cron.ts similarity index 100% rename from packages/nestjs/src/cron-decorator.ts rename to packages/nestjs/src/decorators/sentry-cron.ts diff --git a/packages/nestjs/src/span-decorator.ts b/packages/nestjs/src/decorators/sentry-traced.ts similarity index 100% rename from packages/nestjs/src/span-decorator.ts rename to packages/nestjs/src/decorators/sentry-traced.ts diff --git a/packages/nestjs/src/error-decorator.ts b/packages/nestjs/src/decorators/with-sentry.ts similarity index 94% rename from packages/nestjs/src/error-decorator.ts rename to packages/nestjs/src/decorators/with-sentry.ts index bf1fd08d8cee..6b02d73a94c8 100644 --- a/packages/nestjs/src/error-decorator.ts +++ b/packages/nestjs/src/decorators/with-sentry.ts @@ -1,5 +1,5 @@ import { captureException } from '@sentry/core'; -import { isExpectedError } from './helpers'; +import { isExpectedError } from '../helpers'; /** * A decorator to wrap user-defined exception filters and add Sentry error reporting. diff --git a/packages/nestjs/src/index.ts b/packages/nestjs/src/index.ts index b30fe547103b..71fb1ae4f78c 100644 --- a/packages/nestjs/src/index.ts +++ b/packages/nestjs/src/index.ts @@ -2,6 +2,6 @@ export * from '@sentry/node'; export { init } from './sdk'; -export { SentryTraced } from './span-decorator'; -export { SentryCron } from './cron-decorator'; -export { WithSentry } from './error-decorator'; +export { SentryTraced } from './decorators/sentry-traced'; +export { SentryCron } from './decorators/sentry-cron'; +export { WithSentry } from './decorators/with-sentry'; From 918c011c693661b58b918bb1c302eb457397bef3 Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:41:19 -0400 Subject: [PATCH 29/64] feat(replay): Add layout shift to CLS replay data (#13386) We want to show the score for each layout shift as well as the all the nodes that contributed to the score, so we're adding a new `attributions` object to our web vitals data Relates to https://github.com/getsentry/sentry/issues/69881 --------- Co-authored-by: Billy Vong --- .../utils/replayEventTemplates.ts | 1 + .../tests/fixtures/ReplayRecordingData.ts | 2 +- .../tests/fixtures/ReplayRecordingData.ts | 1 + .../replay-internal/src/types/performance.ts | 4 ++ .../src/util/createPerformanceEntries.ts | 47 +++++++++++++++---- .../unit/util/createPerformanceEntry.test.ts | 8 ++-- 6 files changed, 48 insertions(+), 15 deletions(-) diff --git a/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts b/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts index f4defc27182c..e711ea3bb0bb 100644 --- a/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts +++ b/dev-packages/browser-integration-tests/utils/replayEventTemplates.ts @@ -141,6 +141,7 @@ export const expectedCLSPerformanceSpan = { data: { value: expect.any(Number), nodeIds: expect.any(Array), + attributions: expect.any(Array), rating: expect.any(String), size: expect.any(Number), }, diff --git a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/tests/fixtures/ReplayRecordingData.ts b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/tests/fixtures/ReplayRecordingData.ts index e7fd943c0f08..f65efa7cca2d 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/tests/fixtures/ReplayRecordingData.ts +++ b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/tests/fixtures/ReplayRecordingData.ts @@ -219,7 +219,7 @@ export const ReplayRecordingData = [ data: { value: expect.any(Number), size: expect.any(Number), - nodeId: 16, + nodeIds: [16], }, }, }, diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts index 1b054c099b3d..76abdb1fa6b5 100644 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts +++ b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/tests/fixtures/ReplayRecordingData.ts @@ -240,6 +240,7 @@ export const ReplayRecordingData = [ size: expect.any(Number), rating: expect.any(String), nodeIds: expect.any(Array), + attributions: expect.any(Array), }, }, }, diff --git a/packages/replay-internal/src/types/performance.ts b/packages/replay-internal/src/types/performance.ts index 6b264a44ee9c..7a60e51684f3 100644 --- a/packages/replay-internal/src/types/performance.ts +++ b/packages/replay-internal/src/types/performance.ts @@ -111,6 +111,10 @@ export interface WebVitalData { * The recording id of the web vital nodes. -1 if not found */ nodeIds?: number[]; + /** + * The layout shifts of a CLS metric + */ + attributions?: { value: number; sources?: number[] }[]; } /** diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index d55c2269d0f4..830f878dc8ea 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -43,7 +43,13 @@ export interface Metric { * The array may also be empty if the metric value was not based on any * entries (e.g. a CLS value of 0 given no layout shifts). */ - entries: PerformanceEntry[] | PerformanceEventTiming[]; + entries: PerformanceEntry[] | LayoutShift[]; +} + +interface LayoutShift extends PerformanceEntry { + value: number; + sources: LayoutShiftAttribution[]; + hadRecentInput: boolean; } interface LayoutShiftAttribution { @@ -52,6 +58,11 @@ interface LayoutShiftAttribution { currentRect: DOMRectReadOnly; } +interface Attribution { + value: number; + nodeIds?: number[]; +} + /** * Handler creater for web vitals */ @@ -187,22 +198,32 @@ export function getLargestContentfulPaint(metric: Metric): ReplayPerformanceEntr return getWebVital(metric, 'largest-contentful-paint', node); } +function isLayoutShift(entry: PerformanceEntry | LayoutShift): entry is LayoutShift { + return (entry as LayoutShift).sources !== undefined; +} + /** * Add a CLS event to the replay based on a CLS metric. */ export function getCumulativeLayoutShift(metric: Metric): ReplayPerformanceEntry { - const lastEntry = metric.entries[metric.entries.length - 1] as - | (PerformanceEntry & { sources?: LayoutShiftAttribution[] }) - | undefined; + const layoutShifts: Attribution[] = []; const nodes: Node[] = []; - if (lastEntry && lastEntry.sources) { - for (const source of lastEntry.sources) { - if (source.node) { - nodes.push(source.node); + for (const entry of metric.entries) { + if (isLayoutShift(entry)) { + const nodeIds = []; + for (const source of entry.sources) { + if (source.node) { + nodes.push(source.node); + const nodeId = record.mirror.getId(source.node); + if (nodeId) { + nodeIds.push(nodeId); + } + } } + layoutShifts.push({ value: entry.value, nodeIds }); } } - return getWebVital(metric, 'cumulative-layout-shift', nodes); + return getWebVital(metric, 'cumulative-layout-shift', nodes, layoutShifts); } /** @@ -226,7 +247,12 @@ export function getInteractionToNextPaint(metric: Metric): ReplayPerformanceEntr /** * Add an web vital event to the replay based on the web vital metric. */ -function getWebVital(metric: Metric, name: string, nodes: Node[] | undefined): ReplayPerformanceEntry { +function getWebVital( + metric: Metric, + name: string, + nodes: Node[] | undefined, + attributions?: Attribution[], +): ReplayPerformanceEntry { const value = metric.value; const rating = metric.rating; @@ -242,6 +268,7 @@ function getWebVital(metric: Metric, name: string, nodes: Node[] | undefined): R size: value, rating, nodeIds: nodes ? nodes.map(node => record.mirror.getId(node)) : undefined, + attributions, }, }; diff --git a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts index d85698d1be1d..b82a8941269d 100644 --- a/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts +++ b/packages/replay-internal/test/unit/util/createPerformanceEntry.test.ts @@ -83,7 +83,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'largest-contentful-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, rating: 'good', size: 5108.299, nodeIds: undefined }, + data: { value: 5108.299, rating: 'good', size: 5108.299, nodeIds: undefined, attributions: undefined }, }); }); }); @@ -103,7 +103,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'cumulative-layout-shift', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [] }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: [], attributions: [] }, }); }); }); @@ -123,7 +123,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'first-input-delay', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: undefined, attributions: undefined }, }); }); }); @@ -143,7 +143,7 @@ describe('Unit | util | createPerformanceEntries', () => { name: 'interaction-to-next-paint', start: 1672531205.108299, end: 1672531205.108299, - data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: undefined }, + data: { value: 5108.299, size: 5108.299, rating: 'good', nodeIds: undefined, attributions: undefined }, }); }); }); From 7d331fa19e26ba5664f11e11da5ef52d7ede8c8c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:22:00 +0200 Subject: [PATCH 30/64] ref: Add external contributor to CHANGELOG.md (#13511) This PR adds the external contributor to the CHANGELOG.md file, so that they are credited for their contribution. See #13484 Co-authored-by: Lms24 <8420481+Lms24@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8232b230c17c..c4fb77f95837 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott -Work in this release was contributed by @leopoldkristjansson. Thank you for your contribution! +Work in this release was contributed by @leopoldkristjansson and @filips123. Thank you for your contributions! ## 8.27.0 From 780992d6c10b75018695db4c756198e57099aa51 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Thu, 29 Aug 2024 16:02:38 +0200 Subject: [PATCH 31/64] deps: Bump bundler plugins (#13515) Includes a few fixes and stuff. Fixes https://github.com/getsentry/sentry-javascript/issues/13504 --- .github/dependabot.yml | 3 + .../debug-id-sourcemaps/package.json | 2 +- packages/astro/package.json | 2 +- packages/gatsby/package.json | 2 +- packages/nextjs/package.json | 2 +- packages/nuxt/package.json | 2 +- packages/solidstart/package.json | 2 +- packages/sveltekit/package.json | 2 +- yarn.lock | 129 +++--------------- 9 files changed, 30 insertions(+), 116 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 33d8c9ed27c0..652d9daa2e39 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,6 +15,9 @@ updates: allow: - dependency-name: "@sentry/cli" - dependency-name: "@sentry/vite-plugin" + - dependency-name: "@sentry/webpack-plugin" + - dependency-name: "@sentry/rollup-plugin" + - dependency-name: "@sentry/esbuild-plugin" - dependency-name: "@opentelemetry/*" - dependency-name: "@prisma/instrumentation" - dependency-name: "opentelemetry-instrumentation-fetch-node" diff --git a/dev-packages/e2e-tests/test-applications/debug-id-sourcemaps/package.json b/dev-packages/e2e-tests/test-applications/debug-id-sourcemaps/package.json index ed70d21cae2b..bc61532b71fa 100644 --- a/dev-packages/e2e-tests/test-applications/debug-id-sourcemaps/package.json +++ b/dev-packages/e2e-tests/test-applications/debug-id-sourcemaps/package.json @@ -15,7 +15,7 @@ "devDependencies": { "rollup": "^4.0.2", "vitest": "^0.34.6", - "@sentry/rollup-plugin": "2.14.2" + "@sentry/rollup-plugin": "2.22.3" }, "volta": { "extends": "../../package.json" diff --git a/packages/astro/package.json b/packages/astro/package.json index 4b7c80993dfb..82c73d8a5a0a 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -61,7 +61,7 @@ "@sentry/node": "8.27.0", "@sentry/types": "8.27.0", "@sentry/utils": "8.27.0", - "@sentry/vite-plugin": "^2.20.1" + "@sentry/vite-plugin": "^2.22.3" }, "devDependencies": { "astro": "^3.5.0", diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index b588a4daa156..8cfaccb63c48 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -49,7 +49,7 @@ "@sentry/react": "8.27.0", "@sentry/types": "8.27.0", "@sentry/utils": "8.27.0", - "@sentry/webpack-plugin": "2.16.0" + "@sentry/webpack-plugin": "2.22.3" }, "peerDependencies": { "gatsby": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 593a23c5d133..2d9e110b078f 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -78,7 +78,7 @@ "@sentry/types": "8.27.0", "@sentry/utils": "8.27.0", "@sentry/vercel-edge": "8.27.0", - "@sentry/webpack-plugin": "2.20.1", + "@sentry/webpack-plugin": "2.22.3", "chalk": "3.0.0", "resolve": "1.22.8", "rollup": "3.29.4", diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index bf6e48bbf1d7..16c9d46754d5 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -49,7 +49,7 @@ "@sentry/opentelemetry": "8.27.0", "@sentry/types": "8.27.0", "@sentry/utils": "8.27.0", - "@sentry/vite-plugin": "2.20.1", + "@sentry/vite-plugin": "2.22.3", "@sentry/vue": "8.27.0" }, "devDependencies": { diff --git a/packages/solidstart/package.json b/packages/solidstart/package.json index 4fb81cc25c2f..efb530697ff6 100644 --- a/packages/solidstart/package.json +++ b/packages/solidstart/package.json @@ -73,7 +73,7 @@ "@sentry/solid": "8.27.0", "@sentry/types": "8.27.0", "@sentry/utils": "8.27.0", - "@sentry/vite-plugin": "2.19.0" + "@sentry/vite-plugin": "2.22.3" }, "devDependencies": { "@solidjs/router": "^0.13.4", diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index f332530c338c..f5af15b667d3 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -46,7 +46,7 @@ "@sentry/svelte": "8.27.0", "@sentry/types": "8.27.0", "@sentry/utils": "8.27.0", - "@sentry/vite-plugin": "2.22.0", + "@sentry/vite-plugin": "2.22.3", "magic-string": "0.30.7", "magicast": "0.2.8", "sorcery": "0.11.0" diff --git a/yarn.lock b/yarn.lock index a1a060119a64..6f2432d7054a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8175,75 +8175,18 @@ fflate "^0.4.4" mitt "^3.0.0" -"@sentry/babel-plugin-component-annotate@2.16.0": - version "2.16.0" - resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.16.0.tgz#c831713b85516fb3f9da2985836ddf444dc634e6" - integrity sha512-+uy1qPkA5MSNgJ0L9ur/vNTydfdHwHnBX2RQ+0thsvkqf90fU788YjkkXwUiBBNuqNyI69JiOW6frixAWy7oUg== +"@sentry/babel-plugin-component-annotate@2.22.3": + version "2.22.3" + resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.3.tgz#de4970d51a54ef52b21f0d6ec49bd06bf37753c1" + integrity sha512-OlHA+i+vnQHRIdry4glpiS/xTOtgjmpXOt6IBOUqynx5Jd/iK1+fj+t8CckqOx9wRacO/hru2wfW/jFq0iViLg== -"@sentry/babel-plugin-component-annotate@2.19.0": - version "2.19.0" - resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.19.0.tgz#70dcccb336bcec24148e1c9cd4e37724cebf5673" - integrity sha512-N2k8cMYu/7X6mzAH5j6bMeNcXQBJLL0lVAF63TDS57hUiT1v2uEqbeYFdH2CZBHb2LepLbMRXmvErIwy76FLTw== - -"@sentry/babel-plugin-component-annotate@2.20.1": - version "2.20.1" - resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.20.1.tgz#204c63ed006a048f48f633876e1b8bacf87a9722" - integrity sha512-4mhEwYTK00bIb5Y9UWIELVUfru587Vaeg0DQGswv4aIRHIiMKLyNqCEejaaybQ/fNChIZOKmvyqXk430YVd7Qg== - -"@sentry/babel-plugin-component-annotate@2.22.0": - version "2.22.0" - resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.0.tgz#a7e1cc99d1a738d1eb17757341dff4db3a93c2dc" - integrity sha512-UzH+NNhgnOo6UFku3C4TEz+pO/yDcIA5FKTJvLbJ7lQwAjsqLs3DZWm4cCA08skICb8mULArF6S/dn5/butVCA== - -"@sentry/bundler-plugin-core@2.16.0": - version "2.16.0" - resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.16.0.tgz#0c33e7a054fb56e43bd160ac141f71dfebf6dda5" - integrity sha512-dhgIZsIR3L9KnE2OO5JJm6hPtStAjEPYKQsZzxRr69uVhd9xAvfXeXr0afKVNVEcIDksas6yMgHqwQ2wOXFIAg== - dependencies: - "@babel/core" "^7.18.5" - "@sentry/babel-plugin-component-annotate" "2.16.0" - "@sentry/cli" "^2.22.3" - dotenv "^16.3.1" - find-up "^5.0.0" - glob "^9.3.2" - magic-string "0.27.0" - unplugin "1.0.1" - -"@sentry/bundler-plugin-core@2.19.0": - version "2.19.0" - resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.19.0.tgz#c21935ff5aea9daccfa4c9e0db405aecdec292f6" - integrity sha512-PGTwpue2k4HnLlCuvLeg+cILPWHJorzheNq8KVlXed8mpb8kxKeY9EWQFxBqPS+XyktOMAxZmCMZfKdnHNaJVQ== - dependencies: - "@babel/core" "^7.18.5" - "@sentry/babel-plugin-component-annotate" "2.19.0" - "@sentry/cli" "^2.22.3" - dotenv "^16.3.1" - find-up "^5.0.0" - glob "^9.3.2" - magic-string "0.30.8" - unplugin "1.0.1" - -"@sentry/bundler-plugin-core@2.20.1": - version "2.20.1" - resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.20.1.tgz#c9dd35e2177a4c22ecf675558eb84fbc2607e465" - integrity sha512-6ipbmGzHekxeRCbp7eoefr6bdd/lW4cNA9eNnrmd9+PicubweGaZZbH2NjhFHsaxzgOezwipDHjrTaap2kTHgw== - dependencies: - "@babel/core" "^7.18.5" - "@sentry/babel-plugin-component-annotate" "2.20.1" - "@sentry/cli" "^2.22.3" - dotenv "^16.3.1" - find-up "^5.0.0" - glob "^9.3.2" - magic-string "0.30.8" - unplugin "1.0.1" - -"@sentry/bundler-plugin-core@2.22.0": - version "2.22.0" - resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.22.0.tgz#6a67761ff5bc0dc897e56acba0b12547bc623e14" - integrity sha512-/xXN8o7565WMsewBnQFfjm0E5wqhYsegg++HJ5RjrY/cTM4qcd/ven44GEMxqGFJitZizvkk3NHszaHylzcRUw== +"@sentry/bundler-plugin-core@2.22.3": + version "2.22.3" + resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.22.3.tgz#f8c0a25321216ae9777749c1a4b9d982ae1ec2e1" + integrity sha512-DeoUl0WffcqZZRl5Wy9aHvX4WfZbbWt0QbJ7NJrcEViq+dRAI2FQTYECFLwdZi5Gtb3oyqZICO+P7k8wDnzsjQ== dependencies: "@babel/core" "^7.18.5" - "@sentry/babel-plugin-component-annotate" "2.22.0" + "@sentry/babel-plugin-component-annotate" "2.22.3" "@sentry/cli" "^2.33.1" dotenv "^16.3.1" find-up "^5.0.0" @@ -8321,7 +8264,7 @@ resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.33.1.tgz#2d00b38a2dd9f3355df91825582ada3ea0034e86" integrity sha512-8VyRoJqtb2uQ8/bFRKNuACYZt7r+Xx0k2wXRGTyH05lCjAiVIXn7DiS2BxHFty7M1QEWUCMNsb/UC/x/Cu2wuA== -"@sentry/cli@^2.22.3", "@sentry/cli@^2.33.0": +"@sentry/cli@^2.33.0": version "2.33.0" resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.33.0.tgz#5de59f829070ab20d360fae25924f39c55afd8ba" integrity sha512-9MOzQy1UunVBhPOfEuO0JH2ofWAMmZVavTTR/Bo2CkJwI1qjyVF0UKLTXE3l4ujiJnFufOoBsVyKmYWXFerbCw== @@ -8359,45 +8302,20 @@ "@sentry/cli-win32-i686" "2.33.1" "@sentry/cli-win32-x64" "2.33.1" -"@sentry/vite-plugin@2.19.0": - version "2.19.0" - resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.19.0.tgz#c7938fb13eee15036963b87d7b12c4fc851e488b" - integrity sha512-xmntz/bvRwhhU9q2thZas1vQQch9CLMyD8oCfYlNqN57t5XKhIs2dsCU/uS7HCnxIXuuUb/cZtIS7AXVg16fFA== - dependencies: - "@sentry/bundler-plugin-core" "2.19.0" - unplugin "1.0.1" - -"@sentry/vite-plugin@2.20.1", "@sentry/vite-plugin@^2.20.1": - version "2.20.1" - resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.20.1.tgz#80d5639fca3f68fbf81c316883272ffb34dbc544" - integrity sha512-IOYAJRcV+Uqn0EL8rxcoCvE77PbtGzKjP+B6iIgDZ229AWbpfbpWY8zHCcufwdDzb5PtgOhWWHT74iAsNyij/A== +"@sentry/vite-plugin@2.22.3", "@sentry/vite-plugin@^2.22.3": + version "2.22.3" + resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.22.3.tgz#b52802412b6f3d8e3e56742afc9624d9babae5b6" + integrity sha512-+5bsLFRKOZzBp68XigoNE1pJ3tJ4gt2jXluApu54ui0N/yjfqGQ7LQTD7nL4tmJvB5Agwi0e7M7+fcxe9gSgBA== dependencies: - "@sentry/bundler-plugin-core" "2.20.1" + "@sentry/bundler-plugin-core" "2.22.3" unplugin "1.0.1" -"@sentry/vite-plugin@2.22.0": - version "2.22.0" - resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-2.22.0.tgz#09743ac390cf8c1609f2fa6d5424548d0b6f7928" - integrity sha512-U1dWldo3gb1oDqERgiSM7zexMwAuqiXO/YUO3xVSpWmhoHz2AqxOcfIX1SygW02NF7Ss3ay4qMAta8PbvdsrnQ== +"@sentry/webpack-plugin@2.22.3": + version "2.22.3" + resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-2.22.3.tgz#a9eeb4689c062eb6dc50671c09f06ec6875b9b02" + integrity sha512-Sq1S6bL3nuoTP5typkj+HPjQ13dqftIE8kACAq4tKkXOpWO9bf6HtqcruEQCxMekbWDTdljsrknQ17ZBx2q66Q== dependencies: - "@sentry/bundler-plugin-core" "2.22.0" - unplugin "1.0.1" - -"@sentry/webpack-plugin@2.16.0": - version "2.16.0" - resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-2.16.0.tgz#4764577edb10c9575a8b4ce03135493f995f56b9" - integrity sha512-BeKLmtK4OD9V3j92fm/lm6yp+++s2U5Uf17HwNFGt39PEOq+wUDISsx0dhXA5Qls2Bg3WhguDK71blCaVefMeg== - dependencies: - "@sentry/bundler-plugin-core" "2.16.0" - unplugin "1.0.1" - uuid "^9.0.0" - -"@sentry/webpack-plugin@2.20.1": - version "2.20.1" - resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-2.20.1.tgz#285d325a0a1bd0a534126b97e0190da9486ff7f6" - integrity sha512-U6LzoE09Ndt0OCWROoRaZqqIHGxyMRdKpBhbqoBqyyfVwXN/zGW3I/cWZ1e8rreiKFj+2+c7+X0kOS+NGMTUrg== - dependencies: - "@sentry/bundler-plugin-core" "2.20.1" + "@sentry/bundler-plugin-core" "2.22.3" unplugin "1.0.1" uuid "^9.0.0" @@ -23508,13 +23426,6 @@ magic-string@0.26.2: dependencies: sourcemap-codec "^1.4.8" -magic-string@0.27.0: - version "0.27.0" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" - integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== - dependencies: - "@jridgewell/sourcemap-codec" "^1.4.13" - magic-string@0.30.7: version "0.30.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.7.tgz#0cecd0527d473298679da95a2d7aeb8c64048505" From 03d67c9d6a414628e0cbf56049f127f5ff505f7b Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 29 Aug 2024 16:46:57 +0200 Subject: [PATCH 32/64] core(lint): Add custom `no-focused-tests` and `no-skipped-tests` rules (#13461) Adds two simple custom rules to ignore `(it|test|describe).(skip|only)`. These rules now also flag vitest-based `skip` and `only` functions but led to duplications with the two rules from `eslint-plugin-jest`. So this patch also disables the jest versions in favour of the custom rules. To be clear, the custom rules are likely a bit less robust than the jest/vitest version but until we can use the actual vitest plugin, I think it's fine to stay with our custom version. --- .../core/test/lib/transports/offline.test.ts | 2 +- packages/eslint-config-sdk/src/base.js | 20 ++--------- packages/eslint-plugin-sdk/src/index.js | 2 ++ .../src/rules/no-focused-tests.js | 32 ++++++++++++++++++ .../src/rules/no-skipped-tests.js | 33 +++++++++++++++++++ .../profiling-node/test/cpu_profiler.test.ts | 2 +- 6 files changed, 71 insertions(+), 20 deletions(-) create mode 100644 packages/eslint-plugin-sdk/src/rules/no-focused-tests.js create mode 100644 packages/eslint-plugin-sdk/src/rules/no-skipped-tests.js diff --git a/packages/core/test/lib/transports/offline.test.ts b/packages/core/test/lib/transports/offline.test.ts index ea3fa13ffd69..738597197178 100644 --- a/packages/core/test/lib/transports/offline.test.ts +++ b/packages/core/test/lib/transports/offline.test.ts @@ -410,7 +410,7 @@ describe('makeOfflineTransport', () => { START_DELAY + 2_000, ); - // eslint-disable-next-line jest/no-disabled-tests + // eslint-disable-next-line @sentry-internal/sdk/no-skipped-tests it.skip( 'Follows the Retry-After header', async () => { diff --git a/packages/eslint-config-sdk/src/base.js b/packages/eslint-config-sdk/src/base.js index 9a6fa807e09f..6daa79eaeed8 100644 --- a/packages/eslint-config-sdk/src/base.js +++ b/packages/eslint-config-sdk/src/base.js @@ -184,24 +184,8 @@ module.exports = { '@sentry-internal/sdk/no-optional-chaining': 'off', '@sentry-internal/sdk/no-nullish-coalescing': 'off', '@typescript-eslint/no-floating-promises': 'off', - }, - }, - { - // Configuration only for test files (this won't apply to utils or other files in test directories) - plugins: ['jest'], - env: { - jest: true, - }, - files: ['test.ts', '*.test.ts', '*.test.tsx', '*.test.js', '*.test.jsx'], - rules: { - // Prevent permanent usage of `it.only`, `fit`, `test.only` etc - // We want to avoid debugging leftovers making their way into the codebase - 'jest/no-focused-tests': 'error', - - // Prevent permanent usage of `it.skip`, `xit`, `test.skip` etc - // We want to avoid debugging leftovers making their way into the codebase - // If there's a good reason to skip a test (e.g. bad flakiness), just add an ignore comment - 'jest/no-disabled-tests': 'error', + '@sentry-internal/sdk/no-focused-tests': 'error', + '@sentry-internal/sdk/no-skipped-tests': 'error', }, }, { diff --git a/packages/eslint-plugin-sdk/src/index.js b/packages/eslint-plugin-sdk/src/index.js index 4390af285609..d7516c343d60 100644 --- a/packages/eslint-plugin-sdk/src/index.js +++ b/packages/eslint-plugin-sdk/src/index.js @@ -15,5 +15,7 @@ module.exports = { 'no-eq-empty': require('./rules/no-eq-empty'), 'no-class-field-initializers': require('./rules/no-class-field-initializers'), 'no-regexp-constructor': require('./rules/no-regexp-constructor'), + 'no-focused-tests': require('./rules/no-focused-tests'), + 'no-skipped-tests': require('./rules/no-skipped-tests'), }, }; diff --git a/packages/eslint-plugin-sdk/src/rules/no-focused-tests.js b/packages/eslint-plugin-sdk/src/rules/no-focused-tests.js new file mode 100644 index 000000000000..008431780da2 --- /dev/null +++ b/packages/eslint-plugin-sdk/src/rules/no-focused-tests.js @@ -0,0 +1,32 @@ +'use strict'; + +/** + * This rule was created to flag usages of the `.only` function in vitest and jest tests. + * Usually, we don't want to commit focused tests as this causes other tests to be skipped. + */ +module.exports = { + meta: { + docs: { + description: "Do not focus tests via `.only` to ensure we don't commit accidentally skip the other tests.", + }, + schema: [], + }, + create: function (context) { + return { + CallExpression(node) { + if ( + node.callee.type === 'MemberExpression' && + node.callee.object.type === 'Identifier' && + ['test', 'it', 'describe'].includes(node.callee.object.name) && + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'only' + ) { + context.report({ + node, + message: "Do not focus tests via `.only` to ensure we don't commit accidentally skip the other tests.", + }); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-sdk/src/rules/no-skipped-tests.js b/packages/eslint-plugin-sdk/src/rules/no-skipped-tests.js new file mode 100644 index 000000000000..2c11e6e071a0 --- /dev/null +++ b/packages/eslint-plugin-sdk/src/rules/no-skipped-tests.js @@ -0,0 +1,33 @@ +'use strict'; + +/** + * This rule was created to flag usages of the `.skip` function in vitest and jest tests. + * Usually, we don't want to commit skipped tests as this causes other tests to be skipped. + * Sometimes, skipping is valid (e.g. flaky tests), in which case, we can simply eslint-disable the rule. + */ +module.exports = { + meta: { + docs: { + description: "Do not skip tests via `.skip` to ensure we don't commit accidentally skipped tests.", + }, + schema: [], + }, + create: function (context) { + return { + CallExpression(node) { + if ( + node.callee.type === 'MemberExpression' && + node.callee.object.type === 'Identifier' && + ['test', 'it', 'describe'].includes(node.callee.object.name) && + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'skip' + ) { + context.report({ + node, + message: "Do not skip tests via `.skip` to ensure we don't commit accidentally skipped tests.", + }); + } + }, + }; + }, +}; diff --git a/packages/profiling-node/test/cpu_profiler.test.ts b/packages/profiling-node/test/cpu_profiler.test.ts index c1086003c1af..1e3903be6fc5 100644 --- a/packages/profiling-node/test/cpu_profiler.test.ts +++ b/packages/profiling-node/test/cpu_profiler.test.ts @@ -316,7 +316,7 @@ describe('Profiler bindings', () => { expect(profile?.measurements?.['memory_footprint']?.values.length).toBeLessThanOrEqual(300); }); - // eslint-disable-next-line jest/no-disabled-tests + // eslint-disable-next-line @sentry-internal/sdk/no-skipped-tests it.skip('includes deopt reason', async () => { // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#52-the-object-being-iterated-is-not-a-simple-enumerable function iterateOverLargeHashTable() { From 09622d61b827db29341e8d34672a8ff67912336d Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 30 Aug 2024 09:38:16 +0200 Subject: [PATCH 33/64] fix(replay): Consider more things as DOM mutations for dead clicks (#13518) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, we only considered `Mutation` style changes of rrweb as "DOM mutations" for dead click detection. However, after closer inspection, there are also some other types of changes that we may consider as DOM mutations. By including these we can hopefully reduce some false positives. Not quite sure how to test this 🤔 It's probably also OK to just ship this, as the worst-case scenario is that we have some false-negatives and do not capture certain things, but our general goal here is to be rather on the cautious side, so I think that is acceptable. Possibly fixes https://github.com/getsentry/sentry-javascript/issues/9755 --- .size-limit.js | 2 +- .../replay-internal/src/coreHandlers/handleClick.ts | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index e75eeb422735..d2211d91d5b0 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -22,7 +22,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), gzip: true, - limit: '73 KB', + limit: '75 KB', }, { name: '@sentry/browser (incl. Tracing, Replay) - with treeshaking flags', diff --git a/packages/replay-internal/src/coreHandlers/handleClick.ts b/packages/replay-internal/src/coreHandlers/handleClick.ts index da07474deebb..029588764610 100644 --- a/packages/replay-internal/src/coreHandlers/handleClick.ts +++ b/packages/replay-internal/src/coreHandlers/handleClick.ts @@ -40,6 +40,17 @@ type IncrementalMouseInteractionRecordingEvent = IncrementalRecordingEvent & { data: { type: MouseInteractions; id: number }; }; +/** Any IncrementalSource for rrweb that we interpret as a kind of mutation. */ +const IncrementalMutationSources = new Set([ + IncrementalSource.Mutation, + IncrementalSource.StyleSheetRule, + IncrementalSource.StyleDeclaration, + IncrementalSource.AdoptedStyleSheet, + IncrementalSource.CanvasMutation, + IncrementalSource.Selection, + IncrementalSource.MediaInteraction, +]); + /** Handle a click. */ export function handleClick(clickDetector: ReplayClickDetector, clickBreadcrumb: Breadcrumb, node: HTMLElement): void { clickDetector.handleClick(clickBreadcrumb, node); @@ -324,7 +335,7 @@ export function updateClickDetectorForRecordingEvent(clickDetector: ReplayClickD } const { source } = event.data; - if (source === IncrementalSource.Mutation) { + if (IncrementalMutationSources.has(source)) { clickDetector.registerMutation(event.timestamp); } From bb45dd276ea0a19afe362ae66f13f31cb549a1cc Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 30 Aug 2024 09:53:50 +0200 Subject: [PATCH 34/64] fix(browser): Remove faulty LCP, FCP and FP normalization logic (#13502) Remove incorrect normalization logic we applied to LCP, FCP and FP web vital measurements. With this change, we no longer alter the three web vital values but report directly what we received from the web-vitals library. Add a span attribute, `performance.timeOrigin` (feel free to suggest better names) to the pageload root span. This attribute contains the `timeOrigin` value we determine in the SDK. This value [should be used](https://developer.mozilla.org/en-US/docs/Web/API/Performance/timeOrigin) to base performance measurements on. --- CHANGELOG.md | 11 +++ .../metrics/web-vitals-lcp/template.html | 2 +- .../tracing/metrics/web-vitals-lcp/test.ts | 2 +- .../web-vitals/assets/sentry-logo-600x179.png | Bin 0 -> 16118 bytes .../tracing/metrics/web-vitals/template.html | 11 +++ .../suites/tracing/metrics/web-vitals/test.ts | 81 ++++++++++++++++++ .../tests/transactions.test.ts | 1 + .../src/metrics/browserMetrics.ts | 26 ++---- 8 files changed, 111 insertions(+), 23 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/assets/sentry-logo-600x179.png create mode 100644 dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c4fb77f95837..d330fdf29e78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,17 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +### Important Changes + +- **fix(browser): Remove faulty LCP, FCP and FP normalization logic (#13502)** + +This release fixes a bug in the `@sentry/browser` package and all SDKs depending on this package (e.g. `@sentry/react` +or `@sentry/nextjs`) that caused the SDK to send incorrect web vital values for the LCP, FCP and FP vitals. The SDK +previously incorrectly processed the original values as they were reported from the browser. When updating your SDK to +this version, you might experience an increase in LCP, FCP and FP values, which potentially leads to a decrease in your +performance score in the Web Vitals Insights module in Sentry. This is because the previously reported values were +smaller than the actually measured values. We apologize for the inconvenience! + Work in this release was contributed by @leopoldkristjansson and @filips123. Thank you for your contributions! ## 8.27.0 diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/template.html b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/template.html index caf4b8f2deab..502f4dde80c2 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/template.html +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/template.html @@ -5,7 +5,7 @@
- + diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts index f79505c6105a..2e215c728ecf 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts @@ -11,7 +11,7 @@ sentryTest('should capture a LCP vital with element details.', async ({ browserN } page.route('**', route => route.continue()); - page.route('**/path/to/image.png', async (route: Route) => { + page.route('**/my/image.png', async (route: Route) => { return route.fulfill({ path: `${__dirname}/assets/sentry-logo-600x179.png` }); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/assets/sentry-logo-600x179.png b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/assets/sentry-logo-600x179.png new file mode 100644 index 0000000000000000000000000000000000000000..353b7233d6bfa4f026f9998cacfa4add4bba9274 GIT binary patch literal 16118 zcmeHu^+S})7xyl#gwjYzhlrF&cOzZW-Q5k+0s<-_QZ6msEZvPD-L-UgN%#AB@AcmA z-|)V#KOoD_GtbPKGc)IWKA-bZNkJ0*G0|fX2!t*zC9VttA!q`>SD+vPKh?a9D}n#O zF3OUkpprq7Z4ih8BrPtY>Z!k<;+?FcR@FVl+at$>|46kYBS2WzfXAwjotEiQCvxwN z*ZVTy(;HRsciU<{RKJwo3YUIbdh#fEBMa>kME){Q_YpkV)ix;xAF}p61LdWZjgI-( ziUSKpHf}qMuT?`l(C1@rf@${?i~8P&hde#E&gTm@ls{=8!2wuU2%^F$C}2t~I&e69 zA_VuJj}*VZ1F>lS!UDeg>jTh`!Wh9AMukMb{6Ei8Ajziw*FqRYTuvBGy;GPb`QM%? zAf1LiY=8OyUiLl=HLUFuVP5e6zKoFZ9n$}Fi^T$F(ZoiKbNqXBe?J{WGxXnpDIioY zD3BP8({U7jkN=5Eg~Km*M`? z2k>23+gFRf%<@mS*tl3hcq~e&w2=SC2p9+LzZd^sV;uD-sqa-;Pt2e5Kxz=48N4hp z(6`Nsdmupg(;EI6u(eYy`IF7l=E>5o7DbeJa}PlZ-L3fKAu!0Q8n5nOw;84aoGlSe z(9AqaI70`#97kDSZ&G8OeQ|*9n=>=o%tcy%=UB3PaBaM54l$KLL4 zAtzmPFy6nj7R3R)2WKgAz}wr~BuWVD0%U03`P~35RM>)`@pfY16SLEYKaTz05isY6 z)fyEW2BS1Y6oUxvy~yR%eI))ZGUf~0n49RMf9EX>0xX0eOk60f74dRD@yd68J zMy{IQCq*xb@e;2@FC~i3@O++)+WK#D$JD^C5uLOeyT=DNhLSpW<_j~_?SayJsrRZu zScuehqdFz=)ay9^jvgNNdwUNEeq-f^&l7kr66aujn7~_*?J;rr%nnWyu_^}yZ z#iu0I-F^CR8(Rwg-u$ntzs{(<-$Jr~38^hy;*pHVQKVrFrCMp4(*zw&SzqMj-`&+X zOj=Sa|GRP1KmImeaD(Obb_1rn!xbW^@zXPFn9P0u{jbLp=Hm=@a4wuyWpjSvblE5R z_o5~GuK!un1vr%+_i)Aa>%SK#-v|*2X2zFM(7Vfc2lYi_=n?}}60;pVQEPE_e7J5F zVm+h!@AOKP0?s^?+A8BgOv~WQS^J6J@dmC_JL~ZmW^i(`fIgz_kcTuBd#3mlm0GWn z|Bj-8-%A0Rt+ZN{200jo3Ud!I@f0;_3kX=a{y^fSKk0gfAAv-ouemwbK^BGbpNstV zPdu#MK+_HJv?EEPfhQ%KjKjlXUhd}+ro*M|@FEWZ+V9^9^8a0iVaQ*elEQoTP1;+2 z;Y0)kkD5ttz1XbV)hIIAF^JX5NkIf9%|?a|GQ^(@yqf4Bjr`YT_Pzo}eN?+o$L0b? z(C@YXbV^zi>(8YlB&x~KlF8b5IB45aM(pUHuktgChV{={$Ws6}1Fki1-I|;DeHweB z-6xaV^cxv2!R!%pJ}Y{w7IM^*jcVF-2fU_R@4ElB$_TJqvU|o@?cz9^5@yk`1vOvt z7;Nlx4}Ujr5{`$KzM(zm?>?VG^mnuhXkojn&qOL z*I``8T-|v?5Gkyvc-XfZ&Tw<=%Dy%LoPI=kJavzc|TLsFZn5#L0k66DeHIc3h%zMRyc^H^AQgafUfn1 zpazOu?^EB8x5rU%E&H*gScpK@VP z>Z&vHq%G~}d>_-5z8(WSlu_ZnCvTGq>9h1WYlQ{ku$o-m*ZCDM3~#zyB;@y__l!$`&eP&Iq{mxV8rBuI3w z9(MRh!1f$r&Ey;1^=5Elkl_oDS5mKYmE2`??>u*urEeh5Ya?c9!DMQu&K^!@Z+AKk zSu6*Wrfv#KfEM-BL6qcQu}1uUIO|)m@iVx>`}Jjn31FSGx|0_3mLqRDs)J$g4Zb!nFTm*#)#S1bNqj7H;?>@B|&q)U|%$1*Dhd9qYH-=5;(SBz`)EFd_=QFS*MPNQuDb1+LNlo9l5GwP$?n%7m$$yo-8G=&+QfV%aA-J3^7cH!YF8eV-(>8m(F zKb}jve$^R#2*|gumjkSG+{v!VtU&%Sb>?i_k#!!f&h=x$%GLaeV@_^u$g)cY?FJ(f zYcQ?JG}`ELPuVfpQLb!l4m+zf4<6QLEj-G~#sL1Jw-SP)_$uCIF=_`X0z&C{TODT2m1J|4Kab*JIP zcN?0DTm>rHeA_IB->wQy)i zy1J5Ix>52V-nj28SCz#|mJmr}iS@9!-Rsn)@DWzKs?g6VVToa^Kb-v@(YjZ#C(=@; z6}W3)92_3rSX6X%L@*%Ppea`kxbCb?`ooN_yY||z*LNzrYIW7yo55HL>+bk(M^)@S zNQ=h?QVlH16%pEFgpUwxXSr$|R9dk}593dSMm{n*z+5fStr8kiD^wfQI?>nA@rk#B z{ew6HJ41>IYq+iSto>&XkOQX5q;UAzu+rG=_Tjr}Cfn5bXW!1l2?L0jcd3GLKhtcw z$Jh2yv1pors&UTO100(%{2`YZeIt9^!L|7NizDeStN{-AhAb|w8`hkNP0VCzTWyz}lRDv0eQaWd)Z z=eAK=1r!TB*+GtLrc4{kcd%(jJ|;|jZ{tkVj}!)se4MmDeO!)NkO+m=EkoHMQ}t${ z{==V1Ys-`@k}weFRF9I)A~GYRMA0T+CU4RvC~)N!OnM{gRu=4N5L}cV-k1b*!R1GL z_TGtiGXEG`F)4)O+KL2$WP^qf+e7ERy4VdbYv(k5!BP;;&<7>=Mg>KwH*mcOWYjA| z#~;PEr(0A&eRkz3@zM<^Vsf`tSQ}Ol%M2kBheTxf1XWQ)^RNa|?mumpTonghQ_xn+ z{Oljuw!S62`=M7OnmzUGY*cz|$x9*2pFwRWP19{upCw(Ot#lB99fLOg!<>MG$p9+$ zz9?P7CanDH%pn+iHXLh-_>;c9?n#Ii>J5qTCB20p=*X38>D0S%u0qat%q&`vU7mcO z+szO)^i@D3oDvJ$ESU9^GyqFWS=$A0ay6SCytZ zYK#1p6=4Q^u(MD14>aa9d*}5So zcy`QrkGSI#NxrOS-JKKhR)qrD-OgP(75u#)3F0IGFM3WOFXVgiURsA)JOfh_Zj8{nqAWW^kw~72g99i`4>oq7(1tPd+d4b8Yo-ezId2EK}ZH{vMbv4Y}Sw_ffS@_L_UTbKwCJU zgz>oCBUj7C(hZ1Q{E(?&k<`>z4h*px>_x}~>&ePFcPx-vxgVW`WF(x-F@!}&9?0M* z22Y-Q^W`Hw1U~s7!lL?0zBa>rR1$OZ3lh*Z2dIBpP*5CJtLbjPu{DNzBZMNdujc(s zG74hjjAzCg#mH~+q0m{id~)o>6+p66IbURgrmZ-49}aN9GaG`%OBh@ z_<~r23}l~2x3s}IKW~N;4(L>IK1_Sc)Ayf!R{6&vG$|c7G?t^LFVGss6I?mYiX&$o}5#;~rg{PVeS1;_mC+XUyf8zj_hJ1CFSb(J-Ov;VOkh(1egEn*Ttfl@H!+L4P7ddg>nAa zQ#@f(lTH07^v)LpHs?Tr55sYL)Hf`FybR|Je3v3xC3zj|X)03q;8ve2P(%Jat7?IxEkVJC-?@ zSp$OgP0>}#B~|srfEqJ4pCis~R$H;U?Xbk0CpLDg4+glm(X@l7Twb78j68rpv^X{; z!x`mBn(K+qi5Y{d?0cZO#6pjox|GM=UP3DYHl$6^~EQIR??D9 zhzv`t%rv%fF3HSf}9wTO2Z$C~xyxL44aHo9|)b^XUWNbDM{ zr(1$O+T;)T?nSEPbS4b>l6{$zYQq^FVACGoOJF98hR2KNPj!d1uwvuQ%ymI>PH^g0 z#79HWo{`A$)93i)PsM_61g>>8lFb(zUOW)Y`)Ow{nxV(X{q(_K?K(4x;fQo!bxqim z=t3!HDO>0;fzEV;ErkvcC7O-!rkFPm7gvxDKzi^>X6k?_me6aRDVIn?>sjR&NH%-3 z<(X{-G$y*aDx#l3QS@Fs2hlF)Y(RzxZ19{*J&^pS`lZ~ZfBP!L^ zcoMAqu&49DR&11yW8F8P29+*xw_Dlw)QQmg6;?H5wGq{o27;RRJTzXdJnbH@)7m!W z6oV|Her5X{u%LDNYsXGzOO}@JU2(-QjixAO{TndggWSTmWfB+f&$%r-9ST&@@D~l~ zvi$}WFOjhaBFeF9i9!jBECumNZ;<*b)!v&cKwovACEF5e)3M#qLF$8vJ1G1o3w}M0 zr<0{+&BdiU9A|190y43IksqY?V{Q!vZOYy;5*u(&{P%)*ZszduJsLW8pR*_G49iLN zyMmY9CFkmS0wUj;`zj~k+%1(N$oI-9=+b6{deeM$^>ocz%=q`l6q8N0(4@q(t_}?# zTx8RfDQ?)q07X9-Vh8>3@7kr~n>MstTPvVyE&Qy1um`JD&e0PE#o&g1Ibe$ag|0|p z9ltq4$VShWsbJj;Rn#1KWdS3y&GQ}>=Ji*j^ViXYb?g&7XR}IEA}ur{KP##772N3d z%9!3{^H(rWU0yw8-#D!)dEYa3aChf1f&4C^zQ}aLonm}m{ECZ;J3;5BP%q$9+bEZ2 z!5ey`#XuBVmqQa(|E-W4+@K#c1_o0fkqYC{PNNtRP{^HR2k{u~NZ(qkyT9s^qCNWw zE!p&`D5>MHQ==_QVB>u5rSU!a;g#g9eWG5k;aR`#b)x2+f9*(ligv>}cyRwB89AfY zh*ECyu0f4KeVT+?tfleF?K~t$E=&Vow(cbJn?LQ3;o=?+XZtLL=h!|?D&g^jMTST= zBd22?<|lQNwu*%_QVShyBOE{DJ+yS6i-zlRD0t4PA!aZlKL z=adShn4|q^Hzy7-R&U<#^3XbU^T*pC%#EFIl{BX-*fa)`_8sLm?5gL{?>3B^L95|w zLPDKc4@i)yYnJ+|8EIOv@aQx}Nd@eKU>=U{)Cj@)2*08T%i0fUQg~B5^eswxvS{ir zeV929l!AWT+r#ZvvwA#)+yxl$o`mMb!N?)ORwVnHZ@$?GDU6d9PqD7pQe|kpJv1^s zC5Dh#e9sL`HgRwxHfeHcQ;D2Vw7l$eOyLLPJWnJ(>Q;6Dqj)$Xr8CoYIh>~A_L1{N!1!9i;fkgDe>-x^;<;*=iO;@ zAV8r5>tP*3IKizBVM`h;9S>&(qmNU3ov<-Kq+ut94Soj4QjrU{1{C(bQ+NJJ+L0S0 zt1r1$=q!cK9NDHU!pP==2imcHe*b-!=V8JTMd5Y-^1#cUDlXhe`^tUn|lB2lTh%s0j;-Fahi~7gGbuT;zM!U>pOSjS&%=! zTkaJ*Xzq=0OMvxR`}YsH3x;O^dZS}&_zCWPniyu^g;_WsH`cx} z@OJ=tFwC!4YsAo1ti0Xj{{i|r4(5OujC;v)WKxoFgJ2-!6q}gD*P-BpNOfEE$@65# zjpuINc3@#fHaO8Ki3#(%TcN}7y#l$IldnHIMdMe23#=PBkfbL$>lx)Xww252tbgK7 z;j=-wRvqc{4=J3Q$nM|M!IIji-Ovw3SeXsj8cRkBzPvZ4yKsxFp639D3v4|A)a=Lz}{fpcGF4EkNa+_NCC2izjp* z3f7@Z8b-4D8*MDe${47v##ur&k~PZ0IZwb|=1&3Rw03_^J$aYH$!Ed1uDA$Dbz*PFi7SBB7+*eIR{8)%-FVuouoTf-Gv7w>AatacNDSIR9HzjMlA`daug+Y4-R05F z(L;+zn&%yL_X>(jZ8F6-Iztgw)!}kYP4geZ`W>y8mYq?PbgPaEf>Ehtd(k1A^=6v| zj+xe_lem+H+qQ;i-m)mm-;8_S-sB_bRdLzbfS1;Z!0Pp>E_oq-wD|`tE(o`JBYjrS z+Fq~PY*wGde8$}(CfGJTjkFeoI7*MWXx=>AIio{j@Cs79F)JnL1dLFtbfxi+c36<7H?#JN>lhpa4o30RZR@*Zmzw>K*$BPE0r@Ir-i;xa zNh62Su@^0L5}JDQ`nsdb3BI%~7mv8-m8iGWH&2tni<{w%1TBo7zQ^TX?LMkar^Ns= zpKe2R-+0^^5AbN!oCOF>mgnlQvuvwio@)itT=g2HoMREsufTz_>6Nd7Kp?WL-&z20 zv>T4xg|LX~svLBN8Y}OeLrw;ffjav4tHO3QG_Q0~1D;Ipe{4R8KRLA6mBike zq=SI=eW{JQrasz*js`Uzm2-hrqaLwGBBdiHvPW5<{~{=+FI8Hnt$c+Zlzo2vp~N(u zqn39iJvTftF2|_o`wm$tazK*akX3Ds;)3Culjm;N#EgGu$&n!vwJ+6KT^b}(OQKUg zEbQReuMOANK?a zQbfJa81x|tduikGFai85wD_Q$JGjUaYd~dq?j`PD5f(;DmMz3nDKnY4o_1eiu0+Tc zT`rZtwN$yc-w*x$;L3s@0&nKlddt8>R8B?z-YTe)oC^t|xoK&A+_^xP@^7>Xui^d) z8ZR-5zHZ4Rx^#fUORu^`$62RELvVZ2{MQo(`#JZChzsH(BMczAU<7ZEpZFNR)`!-Z z%cs$2SB_;zOE)Z@C0`ZSlBd9s3)SUUYl@dT4q}$v5wyt`o4j-wILj)!wQ#j_iq9)a zM`}q0cf9HliFbK*Sd7r(4|K>=e8mBfn1yP?&B5(k(XN=kYea1U-_N`C8%4rB-k-ip z-u_dho3I7eiYAb2g~Uy_?vT~w0#$^O9Enx2@m(l*Y5%$+L#-r1iCe2|BeM~yG`4E~QwCG4>_k8#z(tJ50;p#`56+*@_b@Sle zu(V=neZg50kMIyHaACt6#i>8#3mxc>^s z)^UnTZBA&~BN?B?D44g1if?k(i#5n7{6IB=DWl$qg*lpKCh^`r1Ussit?715VD4I5 zk*IG~KdC^S2Z&Ko%C`M6uGgfe)6F~7!tf9#mP}T=_4;PoT9c=wX~MkFx;PZYm6|1j z4g4Su&WXH5!~9fho}h8RqA91tO-p3+PD7{dHx{sQ!A`=fxYq(VNI3yZ&i3I7x5xAV z>(wxOjI=wvR^G&*`qrT|=Qa7zkKwE3CM_jjqeK{z%viux%hk8P`Rp62G0Ct-OZXF?!pwDy}aas5|@~Mr; zAc*4>(OhAY)ra2kKvDjLA3R80r=bd5hE2T6DO{RwpB=ceCQZ>*y!k76W|m-E-H^l% z;%GyQkAtObS=y20pm;Bbf7^OS62XQ2a4^Gar`8zLMB6R7sH>;_ z7K69Dqkk_IQMh2~g~tN{)<}jiPibUUA0Eql_~;}e=txW`X8o z)J+5|w@uIasjkZxf(UJe;^TDPK}sJCjFOkMYxWu?9c*U{i-UbJD16kYyWCNAIN%>+ zMOy#i?XrX?dTqFATx*f~D7;n+obT-w7Wa#+&d7?QZC!0@DO=?q4QX+j4&$7^4IVy{ zG-kM{b8V+4XbXDCv6h9gq|0G&K8m0Z|T+~ZzhaB zTkWat@5clm32~6!Mg$%AikOhLln`{Y?lXg0CBOMLVVyAC6zMKrVCMMIYB@MK>>VQ; zV}h1cBM2?(Q%~g4o1~zSBA%I=_lk{yE8=uD$p(7Nr{nqEa&VhpWI8rKnNK6MSYaj3o=kO`j(4ml0~#eB!f7#X9e) z9z1SZFgst!qpJ|J^n21e&wjTk_I|Sav-unm-K)q8350uNW?#EVxdwZ5j2mKmcG`5({q*;|`{Y=SYQBNU~?CGNy{6&+n5^gS&e|qbNi> z#`?>}H6LO|t%Ep(>RjtZ)XZRYG`(;^HvpDLHHKNAod_a<9>%sX6@nffaB)ZsUh);( z8>BOsSwAD6kX*WxiHG_I>u`Vj8v_uIDd3QS&LCn-rHq;ymKLy-VRSSms*qS+aZ&D6f5H6n}?2T>Re`>RK00LcD%*+KPHlG<5 z*r#!ehlpl?NPuSKzU8kmh2)kwDjeD{`0a^8&|(Z-RM5i30LEEuFke^$;P54dbIXWv zo0xwJz8*+?A8pzvPqRNn+16d>GA51J8o{%`YsTp756jRIuG3mA!oyf-|2V{y-pir+ zmK1F{ya}4<4qt0tr**N?Y|d_wpn7P&BVn*6Ssbnh&At0<=kzx~46)HreUrsA*D z9P|~EYbmx)O08crW1rDLet8aNjjvx{7qXww47r);N$b2bY30xia1GY!t??M(>_WG; z2%2DX*|sr0-q8k4C$Zqgi!T^XbdT@Qv@efL;0zaxqd&COmkDD!{3xm0|5h)uU})!$ zOdH-tlp2398^7-~-0^z4NqFiMtve6e=(i9mJ^@9o9$8@B-vtLWyuHVta`BomAriBs zu_H!tzU97@oa);jnyQH!WvviGPOrK57C(IiaB%Be@p?opXf=IMRfkpp70>D5$bTz{ z#p{2>1BexN(1Nlhcoubi!ogjNJQr0tS0%eVhoi$$pHbFi~i};gj<`iG+weL@HZGD;8{bij=eKYzl>3g91UGM!{Mz`&pAHo5O1pJ#g z=bdn2&F-JK*01$@Akt;n?%MfUO{HC$x}-avTA7-`o@QmSd@a!mcYt%9vucVh>V51F zMLm8zcxRJ^9W=p%WHYCrde-i_9M#F-CjDZ{PiB#!6DIvtI8Nob-G7D$k2IMSzG`w%r&8A2OV{AKuxR zB6g!PhVk|p=KIF$xceKSgZmBr94hs8kXSSu!rGL_=HeOI?wLTtLxIGOxYxr3rtQrn zSeg#lG6@r>l?LlAUM}5`tIe)t^e_c#*@O~1*!|GRr7FAT*y$U+ZW1sk~U;>;e!HRX93_cQr(n1PY|xvs5@ z8Tq`Qa?c)~)Txm=`QKb|StYtS7y}0~c!T%DllTV==R8Pz=F$V>@+sCF5{l5wr(?dL zgGCi1f5t;D1*@MW($xZx6s+N2yDH5y!_7!N{}%`KX} zzA1&|iYaFBC*aqZ!Z&Y5m=Wf(`|+`#8vE^SGwB^$bkUP%je4UAF8ZPvR{;z!dbx#C z1oSr_QP<$wHRKzVja#7xx2CC9e7NJftI~f+IYqMZB;$S4?2+6+SOgG=RTgSZZF4~v z(2UT+`G>@@MgJS~)Mg%fz^`F#$|^uWEsJub!W*SAESmJ2j1Z^iBG}YE^upi@o&0<} zYJQ#zaYf4wdOw{dkXeA6SBETzepvU-faj$g*m{s8$lA-x%YckHj3J1aH~UQr3%@i- zk-AL?9Quni-@l)ExGWQAIQNtOJzKpC0TBF_9s8NQJzUVr(Nm?payu;jYlzls*JiO> zeD-t?_Vb(1WdOkg2CHT~)Cqej^1bI-!ON)b+?W}D>}}gdKQD$ZOqB9d6X0K z)qFQ@yUn9kzl6B}GxmT$2&}Jn>bz(2^KFC*twKaDykUjEA3rMKi??R^5a(T!=NgGmZ|N0G)=}ayKS`u*j~K8PpchjNRbuw*4Xw z^X0f2pGY>RUWGp%)sn?yeTChD1fr}sm=GdL+8P?SlvkK`WLCW_HT89K+Zl-;Pa0BC zNs6ad?s=KZMy`4}+AoVz`qJ98e20%k)u#oIj|Q!JK$j^CNo2u%*l$HcjR2kRnCr1Vrkb#LI(uiG2Tzo29ATe|sO#8rn28-6NDdcN*qd%qdh(gqan%~DXI@OY@F_ez@I@+`dKKy(XX1Q0G#MR5nhRG7; zDj8p5UWF&D*`F~_<_hDQ~TW3 zT8_4|k_q7P`u#A&;wPZpj- z&{a^h+Uswi14ggGFka7yhO&NJX3+ecX1+qwqggCH=IXeg%}>6Rte+Z;-STi|JHza? zA4gouM^9of=|VtbNuwe`=~9m9YW1(sv>9A-neE2Rv#l(D=(^p^si938nY#~x({VtJ zCD_y~nz-GD`OvL+`XKx)dq6tBfz-#kKLHvZ+v+p9eCTM79?3sZ-(^!eHtp*d9F#{) z_YSmLXk5^6Q(IAFfzEGw4N~RAGXcn|UHkf>mT2QN(|WAdb}d6nf3j$>b$&=3hrk+y z3p^4rVSPI)VTIy)6a70nAG<@_DaXuI9OxbVLC8^L0Hm&g&x+Q{5d-My-8y8ULqpVn zEN{az=l7VpkQiD=9vlEn^u>u0u}V&ZDxqbX>gfG*ELh|yb(_u;Gs!qRBLg7R9@vmp z2zoijfp(EwVdPqij=i6&Llwr>ay*)cc7!y%ES2}`GgG)dDmEAHh+PP{=BZ31+;%Y* z;GZmr7&)vNtXhasb~XlZgE-27@FHR%-^uN{v5yKDpnX&cQk(6|{A%uOvmt;Sq^FXp z{c40RH5`=o%5#v0*Z=JFYv4-U59LUmK1WN!?JQp!Y@aZ;915gJTrM-XkZ1rd4?OY3 z@T_pePk8Az1gHq@t*eqR|+G z=XJPCV&Vt?t^zPrMX;+t?qjVmKuE{@b%>j~>Qz^Ie0COBWk#+Z`BH7KW{bxfGkB5= zKfMwT%E;mo=lVOe!de~LGpY<_}( zY=Ryv=2)WRUNX8bd-_L54v!P=L!bOdRIe*6{)IW7G^ucE1fYIK@Q9}`YvB4&33+5> z8QGQpTT}f#E@jwml4J@@y8Gj70XbL zJ@%&%%sR78!<6a#;5##M?x?YxneJgAv+0p@>va`(rix|r82M9*WXbtHo~TH*{T_=- zwv@w? zdvjj?gu+dg0`+Oc%!jkS2|@0_FE*KC2eJqQQpB>853~1^{RGxU?#)pxd^y zQ{-o%mR|-kr$djs_@8s#-9_`A|?Bjk};*9+EyY_=*BVSCRGle7G>H!2= z7_^snEY@*f=$QPh^iLg^a1xFs6u37l4#hHTrgeXEw=pqW#3dPZlZaVDs|%J-u@y+> z$NZ{%HH(@g{*enP3l=f_#S6#stWa=Z@;FIaTYo4aM+lJauV!AtH#uzY=u5NMdGiAD z_hizUP%8%wq35O8OHwa@)X$5!*gwUrJ>Uiw+Xl~Ep5suQV$%IvZct}5XBX%+b7E>` z3eyM@%~}wp^MB%k@o)Xda6ClY{P$Y&B;`2fsrID74fMZrc0~1Mo2X*_h767DK9Q60 zdmwUC`Bf?PmT?wib}K;7aZzB3VoE42?y`ePL_1ViKq7SRRrdHfZY#~3p^-cy@-`4n z@IMtCK*d~Try@VbJck zfBp_E4myZrFzn7^%H`Z_OH|g{?eTOh^(W}C4RyjT8zDmM1z(rExuKx4mP z5S~vUVh9R8LEqp2iCt`BtknZEf=>Xh%0*>b9E=$mB2g^jHGTGLjN628QuA+?e@wz- zF8P~@l``tANH7|;gi84-+cn z{MMv@IK86HUvkwn{VDGorbl36(X=k# zYZ4W+8alQRRKmut`os|WUItPso?gMt5@z|fEV(5rlQhwLRFDW&GZ*pWOI$>ynRTs% zC`WdXUo7<`hbA4Hw_*709d}tMhhS$8)@3eBg1B0R;FQb4_pwmRKQ%X%#P8#u{YG+7 zL969G^)tiR%dw(8b#b$b0V|Vp!`i%dfB;M=6hc0=Bd)?v)?>;M`rP@CVc`&MOISHj zV-fuF=yMqPOMnDl9u@lalX^Aj;XV;;E-z}YX*Un391lDkwq`r+zOn9q#$FOF5e$ng z;4TG(gq%`3R@8qSR}ZDK-;ne#iKVivf!;m>btm^L4?yn4$Tb&g2G`-haSS1ZPs{&4 zr^e~jQ&`yuD3-G2pM^Uo{iVB1!ZTQT4E}p;gv3EihmZ>ZCnpN*!b@+nht7A#`p?5phF7hls{Ex3-myP3MUi{?9cK~yB< z0ni<8hleeNp@em>~gtpe3^usaUL64Drc|Tj5QlX!u|<)l=sv z$8J05OccWKW}R2g0ne|98vH3{=Krbd8b1Gh-_XfC*)Z2X)Od?`wQuEZ&4=-2sltAy zE`aYKUnL4!IkhG*{^BBDUo8N7mzuc)DSwsnJSRB-vUKIQ?wL|!nOvKJ{m=b1Y>i-N z0t*qz`g(p7Ewz6JgzP2F zWxMN%)kcDWEHWs6-0D4QWeR}tNb?$zKSCnI6t6%rd;x0YJYr0m$NC%VoR$AONPo=t z@RX~hM1gDbX-mk;H)=X;C4;#<{TgRI@qO<2z=3LKOmcL!+9A|T(3aDKj}9L;w7Snf&T~F Cl9AH@ literal 0 HcmV?d00001 diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/template.html b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/template.html new file mode 100644 index 000000000000..d4c01b121bf7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/template.html @@ -0,0 +1,11 @@ + + + + + + +
+ + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/test.ts new file mode 100644 index 000000000000..3ff09a2862c5 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals/test.ts @@ -0,0 +1,81 @@ +import type { Route } from '@playwright/test'; +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; + +/** + * Bit of an odd test but we previously ran into cases where we would report TTFB > (LCP, FCP, FP) + * This should never happen and this test serves as a regression test for that. + * + * The problem is: We don't always get valid TTFB from the web-vitals library, so we skip the test if that's the case. + * Note: There is another test that covers that we actually report TTFB if it is valid (@see ../web-vitals-lcp/test.ts). + */ +sentryTest('paint web vitals values are greater than TTFB', async ({ browserName, getLocalTestPath, page }) => { + // Only run in chromium to ensure all vitals are present + if (shouldSkipTracingTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + page.route('**', route => route.continue()); + page.route('**/library/image.png', async (route: Route) => { + return route.fulfill({ path: `${__dirname}/assets/sentry-logo-600x179.png` }); + }); + + const url = await getLocalTestPath({ testDir: __dirname }); + const [eventData] = await Promise.all([ + getFirstSentryEnvelopeRequest(page), + page.goto(url), + page.locator('button').click(), + ]); + + expect(eventData.measurements).toBeDefined(); + + const ttfbValue = eventData.measurements?.ttfb?.value; + + if (!ttfbValue) { + // TTFB is unfortunately quite flaky. Sometimes, the web-vitals library doesn't report TTFB because + // responseStart is 0. This seems to happen somewhat randomly, so we just ignore this in that case. + // @see packages/browser-utils/src/metrics/web-vitals/onTTFB + + // logging the skip reason so that we at least can check for that in CI logs + // eslint-disable-next-line no-console + console.log('SKIPPING: TTFB is not reported'); + sentryTest.skip(); + } + + const lcpValue = eventData.measurements?.lcp?.value; + const fcpValue = eventData.measurements?.fcp?.value; + const fpValue = eventData.measurements?.fp?.value; + + expect(lcpValue).toBeDefined(); + expect(fcpValue).toBeDefined(); + expect(fpValue).toBeDefined(); + + // (LCP, FCP, FP) >= TTFB + expect(lcpValue).toBeGreaterThanOrEqual(ttfbValue!); + expect(fcpValue).toBeGreaterThanOrEqual(ttfbValue!); + expect(fpValue).toBeGreaterThanOrEqual(ttfbValue!); +}); + +sentryTest('captures time origin as span attribute', async ({ getLocalTestPath, page }) => { + // Only run in chromium to ensure all vitals are present + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestPath({ testDir: __dirname }); + const [eventData] = await Promise.all([getFirstSentryEnvelopeRequest(page), page.goto(url)]); + + const timeOriginAttribute = eventData.contexts?.trace?.data?.['performance.timeOrigin']; + const transactionStartTimestamp = eventData.start_timestamp; + + expect(timeOriginAttribute).toBeDefined(); + expect(transactionStartTimestamp).toBeDefined(); + + const delta = Math.abs(transactionStartTimestamp! - timeOriginAttribute); + + // The delta should be less than 1ms if this flakes, we should increase the threshold + expect(delta).toBeLessThanOrEqual(1); +}); diff --git a/dev-packages/e2e-tests/test-applications/react-create-hash-router/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/react-create-hash-router/tests/transactions.test.ts index 10442a4a2bde..861b6c420fbb 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-hash-router/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/react-create-hash-router/tests/transactions.test.ts @@ -22,6 +22,7 @@ test('Captures a pageload transaction', async ({ page }) => { 'sentry.origin': 'auto.pageload.react.reactrouter_v6', 'sentry.sample_rate': 1, 'sentry.source': 'route', + 'performance.timeOrigin': expect.any(Number), }, op: 'pageload', span_id: expect.any(String), diff --git a/packages/browser-utils/src/metrics/browserMetrics.ts b/packages/browser-utils/src/metrics/browserMetrics.ts index b71f80df1ff2..066eba1e6839 100644 --- a/packages/browser-utils/src/metrics/browserMetrics.ts +++ b/packages/browser-utils/src/metrics/browserMetrics.ts @@ -354,25 +354,6 @@ export function addPerformanceEntries(span: Span, options: AddPerformanceEntries if (op === 'pageload') { _addTtfbRequestTimeToMeasurements(_measurements); - ['fcp', 'fp', 'lcp'].forEach(name => { - const measurement = _measurements[name]; - if (!measurement || !transactionStartTime || timeOrigin >= transactionStartTime) { - return; - } - // The web vitals, fcp, fp, lcp, and ttfb, all measure relative to timeOrigin. - // Unfortunately, timeOrigin is not captured within the span span data, so these web vitals will need - // to be adjusted to be relative to span.startTimestamp. - const oldValue = measurement.value; - const measurementTimestamp = timeOrigin + msToSec(oldValue); - - // normalizedValue should be in milliseconds - const normalizedValue = Math.abs((measurementTimestamp - transactionStartTime) * 1000); - const delta = normalizedValue - oldValue; - - DEBUG_BUILD && logger.log(`[Measurements] Normalized ${name} from ${oldValue} to ${normalizedValue} (${delta})`); - measurement.value = normalizedValue; - }); - const fidMark = _measurements['mark.fid']; if (fidMark && _measurements['fid']) { // create span for FID @@ -399,7 +380,10 @@ export function addPerformanceEntries(span: Span, options: AddPerformanceEntries setMeasurement(measurementName, measurement.value, measurement.unit); }); - _tagMetricInfo(span); + // Set timeOrigin which denotes the timestamp which to base the LCP/FCP/FP/TTFB measurements on + span.setAttribute('performance.timeOrigin', timeOrigin); + + _setWebVitalAttributes(span); } _lcpEntry = undefined; @@ -604,7 +588,7 @@ function _trackNavigator(span: Span): void { } /** Add LCP / CLS data to span to allow debugging */ -function _tagMetricInfo(span: Span): void { +function _setWebVitalAttributes(span: Span): void { if (_lcpEntry) { DEBUG_BUILD && logger.log('[Measurements] Adding LCP Data'); From c95cd7601ade185d744bd8a07542793d159e2fd5 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 30 Aug 2024 10:10:17 +0200 Subject: [PATCH 35/64] ci: Do not run profiling node jobs on develop (#13531) This takes a lot of resources, and is only very rarely needed. Now, it only always runs profiling node compilation on release branches, and on dedicated profiling-node changes. --- .github/workflows/build.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 576e2e7aa27c..669a4805838e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -500,7 +500,10 @@ jobs: job_profiling_node_unit_tests: name: Node Profiling Unit Tests needs: [job_get_metadata, job_build] - if: needs.job_build.outputs.changed_node == 'true' || needs.job_get_metadata.outputs.changed_profiling_node == 'true' || github.event_name != 'pull_request' + if: | + needs.job_build.outputs.changed_node == 'true' || + needs.job_get_metadata.outputs.changed_profiling_node == 'true' || + github.event_name != 'pull_request' runs-on: ubuntu-latest timeout-minutes: 10 steps: @@ -1125,8 +1128,7 @@ jobs: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && ( (needs.job_get_metadata.outputs.changed_profiling_node == 'true') || - (needs.job_get_metadata.outputs.is_release == 'true') || - (github.event_name != 'pull_request') + (needs.job_get_metadata.outputs.is_release == 'true') ) needs: [job_get_metadata, job_build, job_e2e_prepare] runs-on: ubuntu-20.04 @@ -1290,8 +1292,7 @@ jobs: # Skip precompile unless we are on a release branch as precompile slows down CI times. if: | (needs.job_get_metadata.outputs.changed_profiling_node == 'true') || - (needs.job_get_metadata.outputs.is_release == 'true') || - (github.event_name != 'pull_request') + (needs.job_get_metadata.outputs.is_release == 'true') runs-on: ${{ matrix.os }} container: ${{ matrix.container }} timeout-minutes: 30 From 70ab8a99c22edd67bd327b1b0f7d49ea83775929 Mon Sep 17 00:00:00 2001 From: Leopold Kristjansson Date: Fri, 30 Aug 2024 10:49:30 +0200 Subject: [PATCH 36/64] docs(nuxt): Consistently use `SENTRY_DSN` env var in README examples (#13524) --- packages/nuxt/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nuxt/README.md b/packages/nuxt/README.md index 9049ec7e6691..df41599e45b9 100644 --- a/packages/nuxt/README.md +++ b/packages/nuxt/README.md @@ -96,7 +96,7 @@ Add a `sentry.client.config.(js|ts)` file to the root of your project: import * as Sentry from '@sentry/nuxt'; Sentry.init({ - dsn: process.env.DSN, + dsn: process.env.SENTRY_DSN, }); ``` @@ -107,10 +107,10 @@ Add an `instrument.server.mjs` file to your `public` folder: ```javascript import * as Sentry from '@sentry/nuxt'; -// Only run `init` when DSN is available +// Only run `init` when process.env.SENTRY_DSN is available. if (process.env.SENTRY_DSN) { Sentry.init({ - dsn: process.env.DSN, + dsn: process.env.SENTRY_DSN, }); } ``` From eacf56b0994a6d061452280d7d338502a8e1ff32 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Fri, 30 Aug 2024 10:58:19 +0200 Subject: [PATCH 37/64] test: Unflake remix E2E test (#13534) This flaked e.g. here: https://github.com/getsentry/sentry-javascript/actions/runs/10629179350/job/29465889113 --- .../tests/server-transactions.test.ts | 8 ++++---- .../tests/server-transactions.test.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-express-legacy/tests/server-transactions.test.ts b/dev-packages/e2e-tests/test-applications/create-remix-app-express-legacy/tests/server-transactions.test.ts index b7b770d940dd..9dcf71e9af7f 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-express-legacy/tests/server-transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-express-legacy/tests/server-transactions.test.ts @@ -19,6 +19,10 @@ test('Sends parameterized transaction name to Sentry', async ({ page }) => { }); test('Sends form data with action span to Sentry', async ({ page }) => { + const formdataActionTransaction = waitForTransaction('create-remix-app-express-legacy', transactionEvent => { + return transactionEvent?.spans?.some(span => span.op === 'function.remix.action'); + }); + await page.goto('/action-formdata'); await page.fill('input[name=text]', 'test'); @@ -30,10 +34,6 @@ test('Sends form data with action span to Sentry', async ({ page }) => { await page.locator('button[type=submit]').click(); - const formdataActionTransaction = waitForTransaction('create-remix-app-express-legacy', transactionEvent => { - return transactionEvent?.spans?.some(span => span.op === 'function.remix.action'); - }); - const actionSpan = (await formdataActionTransaction).spans.find(span => span.op === 'function.remix.action'); expect(actionSpan).toBeDefined(); diff --git a/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts b/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts index 9f72da47d67f..b43d538ca683 100644 --- a/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts @@ -19,6 +19,10 @@ test('Sends parameterized transaction name to Sentry', async ({ page }) => { }); test('Sends form data with action span', async ({ page }) => { + const formdataActionTransaction = waitForTransaction('create-remix-app-express', transactionEvent => { + return transactionEvent?.spans?.some(span => span.data && span.data['code.function'] === 'action'); + }); + await page.goto('/action-formdata'); await page.fill('input[name=text]', 'test'); @@ -30,10 +34,6 @@ test('Sends form data with action span', async ({ page }) => { await page.locator('button[type=submit]').click(); - const formdataActionTransaction = waitForTransaction('create-remix-app-express', transactionEvent => { - return transactionEvent?.spans?.some(span => span.data && span.data['code.function'] === 'action'); - }); - const actionSpan = (await formdataActionTransaction).spans.find( span => span.data && span.data['code.function'] === 'action', ); From a455e2b1a2c9845991c929a9829f3abd53bab000 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 11:52:12 +0200 Subject: [PATCH 38/64] ci(deps): Bump denoland/setup-deno from 1.1.4 to 1.4.0 (#13522) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [denoland/setup-deno](https://github.com/denoland/setup-deno) from 1.1.4 to 1.4.0.
Release notes

Sourced from denoland/setup-deno's releases.

1.4.0

What's Changed

Full Changelog: https://github.com/denoland/setup-deno/compare/v1.3.0...1.4.0

v1.3.0

What's Changed

Full Changelog: https://github.com/denoland/setup-deno/compare/v1.2.0...v1.3.0

v1.2.0

What's Changed

New Contributors

Full Changelog: https://github.com/denoland/setup-deno/compare/v1.1.4...v1.2.0

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=denoland/setup-deno&package-manager=github_actions&previous-version=1.1.4&new-version=1.4.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 669a4805838e..cc43c68c0385 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -437,7 +437,7 @@ jobs: with: node-version-file: 'package.json' - name: Set up Deno - uses: denoland/setup-deno@v1.1.4 + uses: denoland/setup-deno@1.4.0 with: deno-version: v1.38.5 - name: Restore caches From f4524238c6a370ce1280d2179d3f208533ecc752 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 09:54:06 +0000 Subject: [PATCH 39/64] ref: Add external contributor to CHANGELOG.md (#13535) This PR adds the external contributor to the CHANGELOG.md file, so that they are credited for their contribution. See #13524 Co-authored-by: lforst <8118419+lforst@users.noreply.github.com> --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d330fdf29e78..090365e69352 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +Work in this release was contributed by @leopoldkristjansson. Thank you for your contribution! + ### Important Changes - **fix(browser): Remove faulty LCP, FCP and FP normalization logic (#13502)** From 01165db19e58c62c8bf1140aa2275eef52d8d779 Mon Sep 17 00:00:00 2001 From: Jonas Date: Fri, 30 Aug 2024 11:26:02 -0400 Subject: [PATCH 40/64] feat(profiling): Expose profiler as top level primitive (#13512) We are about to enter a public beta for continuous profiling, which means it is time to expose this from under the wraps of the integration and align it with how the profiler is exposed in python and iOS SDKs --------- Co-authored-by: Luca Forstner --- packages/astro/src/index.server.ts | 1 + packages/aws-serverless/src/index.ts | 1 + packages/bun/src/index.ts | 1 + packages/core/src/index.ts | 1 + packages/core/src/profiling.ts | 72 +++++++++++++++++++ packages/google-cloud-serverless/src/index.ts | 1 + packages/node/src/index.ts | 1 + packages/profiling-node/src/integration.ts | 14 ++-- .../test/spanProfileUtils.test.ts | 63 +++++++++++----- .../test/spanProfileUtils.worker.test.ts | 5 +- packages/types/src/index.ts | 1 + packages/types/src/profiling.ts | 17 +++++ 12 files changed, 153 insertions(+), 25 deletions(-) create mode 100644 packages/core/src/profiling.ts diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index be3f002dcbb8..0895eb86e364 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -123,6 +123,7 @@ export { withMonitor, withScope, zodErrorsIntegration, + profiler, } from '@sentry/node'; export { init } from './server/sdk'; diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 7b05f8df3a86..27f48c2da0db 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -107,6 +107,7 @@ export { trpcMiddleware, addOpenTelemetryInstrumentation, zodErrorsIntegration, + profiler, } from '@sentry/node'; export { diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index e49e4163af31..d4ca06f85584 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -128,6 +128,7 @@ export { trpcMiddleware, addOpenTelemetryInstrumentation, zodErrorsIntegration, + profiler, } from '@sentry/node'; export { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 792bf3572934..24cea1bea7ca 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -100,6 +100,7 @@ export { sessionTimingIntegration } from './integrations/sessiontiming'; export { zodErrorsIntegration } from './integrations/zoderrors'; export { thirdPartyErrorFilterIntegration } from './integrations/third-party-errors-filter'; export { metrics } from './metrics/exports'; +export { profiler } from './profiling'; export type { MetricData } from '@sentry/types'; export { metricsDefault } from './metrics/exports-default'; export { BrowserMetricsAggregator } from './metrics/browser-aggregator'; diff --git a/packages/core/src/profiling.ts b/packages/core/src/profiling.ts new file mode 100644 index 000000000000..446eebb73671 --- /dev/null +++ b/packages/core/src/profiling.ts @@ -0,0 +1,72 @@ +import type { Profiler, ProfilingIntegration } from '@sentry/types'; +import { logger } from '@sentry/utils'; + +import { getClient } from './currentScopes'; +import { DEBUG_BUILD } from './debug-build'; + +function isProfilingIntegrationWithProfiler( + integration: ProfilingIntegration | undefined, +): integration is ProfilingIntegration { + return ( + !!integration && + typeof integration['_profiler'] !== 'undefined' && + typeof integration['_profiler']['start'] === 'function' && + typeof integration['_profiler']['stop'] === 'function' + ); +} +/** + * Starts the Sentry continuous profiler. + * This mode is exclusive with the transaction profiler and will only work if the profilesSampleRate is set to a falsy value. + * In continuous profiling mode, the profiler will keep reporting profile chunks to Sentry until it is stopped, which allows for continuous profiling of the application. + */ +function startProfiler(): void { + const client = getClient(); + if (!client) { + DEBUG_BUILD && logger.warn('No Sentry client available, profiling is not started'); + return; + } + + const integration = client.getIntegrationByName>('ProfilingIntegration'); + + if (!integration) { + DEBUG_BUILD && logger.warn('ProfilingIntegration is not available'); + return; + } + + if (!isProfilingIntegrationWithProfiler(integration)) { + DEBUG_BUILD && logger.warn('Profiler is not available on profiling integration.'); + return; + } + + integration._profiler.start(); +} + +/** + * Stops the Sentry continuous profiler. + * Calls to stop will stop the profiler and flush the currently collected profile data to Sentry. + */ +function stopProfiler(): void { + const client = getClient(); + if (!client) { + DEBUG_BUILD && logger.warn('No Sentry client available, profiling is not started'); + return; + } + + const integration = client.getIntegrationByName>('ProfilingIntegration'); + if (!integration) { + DEBUG_BUILD && logger.warn('ProfilingIntegration is not available'); + return; + } + + if (!isProfilingIntegrationWithProfiler(integration)) { + DEBUG_BUILD && logger.warn('Profiler is not available on profiling integration.'); + return; + } + + integration._profiler.stop(); +} + +export const profiler: Profiler = { + startProfiler, + stopProfiler, +}; diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 9a501307e79f..f58305c64f6c 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -107,6 +107,7 @@ export { trpcMiddleware, addOpenTelemetryInstrumentation, zodErrorsIntegration, + profiler, } from '@sentry/node'; export { diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 9ef89ab42fb7..5f4adf315fd5 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -127,6 +127,7 @@ export { spanToBaggageHeader, trpcMiddleware, zodErrorsIntegration, + profiler, } from '@sentry/core'; export type { diff --git a/packages/profiling-node/src/integration.ts b/packages/profiling-node/src/integration.ts index 50f240bff732..c1a96015f0c4 100644 --- a/packages/profiling-node/src/integration.ts +++ b/packages/profiling-node/src/integration.ts @@ -9,7 +9,7 @@ import { spanToJSON, } from '@sentry/core'; import type { NodeClient } from '@sentry/node'; -import type { Event, Integration, IntegrationFn, Profile, ProfileChunk, Span } from '@sentry/types'; +import type { Event, IntegrationFn, Profile, ProfileChunk, ProfilingIntegration, Span } from '@sentry/types'; import { LRUMap, logger, uuid4 } from '@sentry/utils'; @@ -159,6 +159,7 @@ interface ChunkData { timer: NodeJS.Timeout | undefined; startTraceID: string; } + class ContinuousProfiler { private _profilerId = uuid4(); private _client: NodeClient | undefined = undefined; @@ -384,12 +385,8 @@ class ContinuousProfiler { } } -export interface ProfilingIntegration extends Integration { - _profiler: ContinuousProfiler; -} - /** Exported only for tests. */ -export const _nodeProfilingIntegration = ((): ProfilingIntegration => { +export const _nodeProfilingIntegration = ((): ProfilingIntegration => { if (DEBUG_BUILD && ![16, 18, 20, 22].includes(NODE_MAJOR)) { logger.warn( `[Profiling] You are using a Node.js version that does not have prebuilt binaries (${NODE_VERSION}).`, @@ -407,7 +404,10 @@ export const _nodeProfilingIntegration = ((): ProfilingIntegration => { const options = client.getOptions(); const mode = - (options.profilesSampleRate === undefined || options.profilesSampleRate === 0) && !options.profilesSampler + (options.profilesSampleRate === undefined || + options.profilesSampleRate === null || + options.profilesSampleRate === 0) && + !options.profilesSampler ? 'continuous' : 'span'; switch (mode) { diff --git a/packages/profiling-node/test/spanProfileUtils.test.ts b/packages/profiling-node/test/spanProfileUtils.test.ts index 4a90caa0f353..f65556f57ab4 100644 --- a/packages/profiling-node/test/spanProfileUtils.test.ts +++ b/packages/profiling-node/test/spanProfileUtils.test.ts @@ -2,10 +2,11 @@ import * as Sentry from '@sentry/node'; import { getMainCarrier } from '@sentry/core'; import type { NodeClientOptions } from '@sentry/node/build/types/types'; +import type { ProfilingIntegration } from '@sentry/types'; import type { ProfileChunk, Transport } from '@sentry/types'; import { GLOBAL_OBJ, createEnvelope, logger } from '@sentry/utils'; import { CpuProfilerBindings } from '../src/cpu_profiler'; -import { type ProfilingIntegration, _nodeProfilingIntegration } from '../src/integration'; +import { _nodeProfilingIntegration } from '../src/integration'; function makeClientWithHooks(): [Sentry.NodeClient, Transport] { const integration = _nodeProfilingIntegration(); @@ -299,7 +300,7 @@ describe('automated span instrumentation', () => { Sentry.setCurrentClient(client); client.init(); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); } @@ -390,7 +391,7 @@ describe('continuous profiling', () => { }); afterEach(() => { const client = Sentry.getClient(); - const integration = client?.getIntegrationByName('ProfilingIntegration'); + const integration = client?.getIntegrationByName>('ProfilingIntegration'); if (integration) { integration._profiler.stop(); @@ -432,7 +433,7 @@ describe('continuous profiling', () => { const transportSpy = jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); } @@ -446,7 +447,7 @@ describe('continuous profiling', () => { expect(profile.client_sdk.version).toEqual(expect.stringMatching(/\d+\.\d+\.\d+/)); }); - it('initializes the continuous profiler and binds the sentry client', () => { + it('initializes the continuous profiler', () => { const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); const [client] = makeContinuousProfilingClient(); @@ -455,14 +456,13 @@ describe('continuous profiling', () => { expect(startProfilingSpy).not.toHaveBeenCalledTimes(1); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); } integration._profiler.start(); expect(integration._profiler).toBeDefined(); - expect(integration._profiler['_client']).toBe(client); }); it('starts a continuous profile', () => { @@ -473,7 +473,7 @@ describe('continuous profiling', () => { client.init(); expect(startProfilingSpy).not.toHaveBeenCalledTimes(1); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); } @@ -490,7 +490,7 @@ describe('continuous profiling', () => { client.init(); expect(startProfilingSpy).not.toHaveBeenCalledTimes(1); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); } @@ -509,7 +509,7 @@ describe('continuous profiling', () => { client.init(); expect(startProfilingSpy).not.toHaveBeenCalledTimes(1); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); } @@ -529,7 +529,7 @@ describe('continuous profiling', () => { client.init(); expect(startProfilingSpy).not.toHaveBeenCalledTimes(1); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); } @@ -548,7 +548,7 @@ describe('continuous profiling', () => { client.init(); expect(startProfilingSpy).not.toHaveBeenCalledTimes(1); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); } @@ -604,7 +604,7 @@ describe('continuous profiling', () => { const transportSpy = jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); } @@ -632,7 +632,7 @@ describe('continuous profiling', () => { }, }); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); } @@ -692,7 +692,7 @@ describe('span profiling mode', () => { Sentry.startInactiveSpan({ forceTransaction: true, name: 'profile_hub' }); expect(startProfilingSpy).toHaveBeenCalled(); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); @@ -703,6 +703,10 @@ describe('span profiling mode', () => { }); }); describe('continuous profiling mode', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + it.each([ ['profilesSampleRate=0', makeClientOptions({ profilesSampleRate: 0 })], ['profilesSampleRate=undefined', makeClientOptions({ profilesSampleRate: undefined })], @@ -739,7 +743,7 @@ describe('continuous profiling mode', () => { jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); } @@ -750,4 +754,31 @@ describe('continuous profiling mode', () => { Sentry.startInactiveSpan({ forceTransaction: true, name: 'profile_hub' }); expect(startProfilingSpy).toHaveBeenCalledTimes(callCount); }); + + it('top level methods proxy to integration', () => { + const client = new Sentry.NodeClient({ + ...makeClientOptions({ profilesSampleRate: undefined }), + dsn: 'https://7fa19397baaf433f919fbe02228d5470@o1137848.ingest.sentry.io/6625302', + tracesSampleRate: 1, + transport: _opts => + Sentry.makeNodeTransport({ + url: 'https://7fa19397baaf433f919fbe02228d5470@o1137848.ingest.sentry.io/6625302', + recordDroppedEvent: () => { + return undefined; + }, + }), + integrations: [_nodeProfilingIntegration()], + }); + + Sentry.setCurrentClient(client); + client.init(); + + const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); + const stopProfilingSpy = jest.spyOn(CpuProfilerBindings, 'stopProfiling'); + + Sentry.profiler.startProfiler(); + expect(startProfilingSpy).toHaveBeenCalledTimes(1); + Sentry.profiler.stopProfiler(); + expect(stopProfilingSpy).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/profiling-node/test/spanProfileUtils.worker.test.ts b/packages/profiling-node/test/spanProfileUtils.worker.test.ts index a119f80292d5..12727aebc954 100644 --- a/packages/profiling-node/test/spanProfileUtils.worker.test.ts +++ b/packages/profiling-node/test/spanProfileUtils.worker.test.ts @@ -9,7 +9,8 @@ jest.setTimeout(10000); import * as Sentry from '@sentry/node'; import type { Transport } from '@sentry/types'; -import { type ProfilingIntegration, _nodeProfilingIntegration } from '../src/integration'; +import { type ProfilingIntegration } from '@sentry/types'; +import { _nodeProfilingIntegration } from '../src/integration'; function makeContinuousProfilingClient(): [Sentry.NodeClient, Transport] { const integration = _nodeProfilingIntegration(); @@ -49,7 +50,7 @@ it('worker threads context', () => { }, }); - const integration = client.getIntegrationByName('ProfilingIntegration'); + const integration = client.getIntegrationByName>('ProfilingIntegration'); if (!integration) { throw new Error('Profiling integration not found'); } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 1022e69ad49e..b100c1e9c26a 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -171,4 +171,5 @@ export type { Metrics, } from './metrics'; export type { ParameterizedString } from './parameterize'; +export type { ContinuousProfiler, ProfilingIntegration, Profiler } from './profiling'; export type { ViewHierarchyData, ViewHierarchyWindow } from './view-hierarchy'; diff --git a/packages/types/src/profiling.ts b/packages/types/src/profiling.ts index 8f5f4cc2e890..9ecba8ced48a 100644 --- a/packages/types/src/profiling.ts +++ b/packages/types/src/profiling.ts @@ -1,6 +1,23 @@ +import type { Client } from './client'; import type { DebugImage } from './debugMeta'; +import type { Integration } from './integration'; import type { MeasurementUnit } from './measurement'; +export interface ContinuousProfiler { + initialize(client: T): void; + start(): void; + stop(): void; +} + +export interface ProfilingIntegration extends Integration { + _profiler: ContinuousProfiler; +} + +export interface Profiler { + startProfiler(): void; + stopProfiler(): void; +} + export type ThreadId = string; export type FrameId = number; export type StackId = number; From 318c1635a4f528a10c3af4014b1f8bf2185890c0 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:20:09 +0200 Subject: [PATCH 41/64] feat(nextjs): Add `bundleSizeOptimizations` to build options (#13323) Extend options that can be added to `withSentryConfig` part of https://github.com/getsentry/sentry-javascript/issues/13012 --------- Co-authored-by: Luca Forstner --- packages/nextjs/src/config/types.ts | 44 +++++++++++++++++++ packages/nextjs/src/config/webpack.ts | 2 +- .../nextjs/src/config/webpackPluginOptions.ts | 3 ++ .../webpack/webpackPluginOptions.test.ts | 17 +++++++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts index 883ba3c26d41..d2e78d87f4ae 100644 --- a/packages/nextjs/src/config/types.ts +++ b/packages/nextjs/src/config/types.ts @@ -307,6 +307,50 @@ export type SentryBuildOptions = { }; }; + /** + * Options to configure various bundle size optimizations related to the Sentry SDK. + */ + bundleSizeOptimizations?: { + /** + * If set to `true`, the Sentry SDK will attempt to tree-shake (remove) any debugging code within itself during the build. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * Setting this option to `true` will disable features like the SDK's `debug` option. + */ + excludeDebugStatements?: boolean; + + /** + * If set to `true`, the Sentry SDK will attempt to tree-shake (remove) code within itself that is related to tracing and performance monitoring. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * **Notice:** Do not enable this when you're using any performance monitoring-related SDK features (e.g. `Sentry.startTransaction()`). + */ + excludeTracing?: boolean; + + /** + * If set to `true`, the Sentry SDK will attempt to tree-shake (remove) code related to the SDK's Session Replay Shadow DOM recording functionality. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * This option is safe to be used when you do not want to capture any Shadow DOM activity via Sentry Session Replay. + */ + excludeReplayShadowDom?: boolean; + + /** + * If set to `true`, the Sentry SDK will attempt to tree-shake (remove) code related to the SDK's Session Replay `iframe` recording functionality. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * You can safely do this when you do not want to capture any `iframe` activity via Sentry Session Replay. + */ + excludeReplayIframe?: boolean; + + /** + * If set to `true`, the Sentry SDK will attempt to tree-shake (remove) code related to the SDK's Session Replay's Compression Web Worker. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * **Notice:** You should only use this option if you manually host a compression worker and configure it in your Sentry Session Replay integration config via the `workerUrl` option. + */ + excludeReplayWorker?: boolean; + }; + /** * Options related to react component name annotations. * Disabled by default, unless a value is set for this option. diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index ecc39f7372dd..8fbc94b42195 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -37,7 +37,7 @@ let showedMissingGlobalErrorWarningMsg = false; * - `plugins`, to add SentryWebpackPlugin * * @param userNextConfig The user's existing nextjs config, as passed to `withSentryConfig` - * @param userSentryWebpackPluginOptions The user's SentryWebpackPlugin config, as passed to `withSentryConfig` + * @param userSentryOptions The user's SentryWebpackPlugin config, as passed to `withSentryConfig` * @returns The function to set as the nextjs config's `webpack` value */ export function constructWebpackConfigFunction( diff --git a/packages/nextjs/src/config/webpackPluginOptions.ts b/packages/nextjs/src/config/webpackPluginOptions.ts index f70862bfe484..1bca9bff49b6 100644 --- a/packages/nextjs/src/config/webpackPluginOptions.ts +++ b/packages/nextjs/src/config/webpackPluginOptions.ts @@ -97,6 +97,9 @@ export function getWebpackPluginOptions( deploy: sentryBuildOptions.release?.deploy, ...sentryBuildOptions.unstable_sentryWebpackPluginOptions?.release, }, + bundleSizeOptimizations: { + ...sentryBuildOptions.bundleSizeOptimizations, + }, _metaOptions: { loggerPrefixOverride: `[@sentry/nextjs - ${prefixInsert}]`, telemetry: { diff --git a/packages/nextjs/test/config/webpack/webpackPluginOptions.test.ts b/packages/nextjs/test/config/webpack/webpackPluginOptions.test.ts index 54d55a179e28..557859b2a7e1 100644 --- a/packages/nextjs/test/config/webpack/webpackPluginOptions.test.ts +++ b/packages/nextjs/test/config/webpack/webpackPluginOptions.test.ts @@ -108,6 +108,23 @@ describe('getWebpackPluginOptions()', () => { }); }); + it('forwards bundleSizeOptimization options', () => { + const buildContext = generateBuildContext({ isServer: false }); + const generatedPluginOptions = getWebpackPluginOptions(buildContext, { + bundleSizeOptimizations: { + excludeTracing: true, + excludeReplayShadowDom: false, + }, + }); + + expect(generatedPluginOptions).toMatchObject({ + bundleSizeOptimizations: { + excludeTracing: true, + excludeReplayShadowDom: false, + }, + }); + }); + it('returns the right `assets` and `ignore` values during the server build', () => { const buildContext = generateBuildContext({ isServer: true }); const generatedPluginOptions = getWebpackPluginOptions(buildContext, {}); From b4112b0ca5d49f87a365cca8d91f04293c4d9f4c Mon Sep 17 00:00:00 2001 From: Matt Huggins Date: Mon, 2 Sep 2024 06:31:08 -0500 Subject: [PATCH 42/64] docs: Fix jsdocs param names (#13540) Before submitting a pull request, please take a look at our [Contributing](https://github.com/getsentry/sentry-javascript/blob/master/CONTRIBUTING.md) guidelines and verify: - [x] If you've added code that should be tested, please add tests. - [x] Ensure your code lints and the test suite passes (`yarn lint`) & (`yarn test`). --- packages/types/src/scope.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts index ccce883d3366..aa51c69035f1 100644 --- a/packages/types/src/scope.ts +++ b/packages/types/src/scope.ts @@ -243,7 +243,7 @@ export interface Scope { /** * Capture a message for this scope. * - * @param exception The exception to capture. + * @param message The message to capture. * @param level An optional severity level to report the message with. * @param hint Optional additional data to attach to the Sentry event. * @returns the id of the captured message. @@ -253,7 +253,7 @@ export interface Scope { /** * Capture a Sentry event for this scope. * - * @param exception The event to capture. + * @param event The event to capture. * @param hint Optional additional data to attach to the Sentry event. * @returns the id of the captured event. */ From 15fcfeaf6c9e2a16229891643953004d99f5c1ea Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 2 Sep 2024 13:59:38 +0200 Subject: [PATCH 43/64] feat(nextjs): Stabilize `captureRequestError` (#13550) --- .../nextjs-13/instrumentation.ts | 2 ++ .../nextjs-14/instrumentation.ts | 2 ++ .../nextjs-15/instrumentation.ts | 2 +- .../nextjs-app-dir/instrumentation.ts | 2 ++ .../nextjs/src/common/captureRequestError.ts | 20 +++++++++---------- packages/nextjs/src/common/index.ts | 3 ++- packages/nextjs/src/index.types.ts | 3 ++- 7 files changed, 21 insertions(+), 13 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/instrumentation.ts b/dev-packages/e2e-tests/test-applications/nextjs-13/instrumentation.ts index 1f889238427c..180685d41b4a 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/instrumentation.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/instrumentation.ts @@ -15,3 +15,5 @@ export function register() { }); } } + +export const onRequestError = Sentry.captureRequestError; diff --git a/dev-packages/e2e-tests/test-applications/nextjs-14/instrumentation.ts b/dev-packages/e2e-tests/test-applications/nextjs-14/instrumentation.ts index 0999fdd8e089..2dd920dfcc73 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-14/instrumentation.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-14/instrumentation.ts @@ -19,3 +19,5 @@ export function register() { }); } } + +export const onRequestError = Sentry.captureRequestError; diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/instrumentation.ts b/dev-packages/e2e-tests/test-applications/nextjs-15/instrumentation.ts index ca4a213e58ba..964f937c439a 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15/instrumentation.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15/instrumentation.ts @@ -10,4 +10,4 @@ export async function register() { } } -export const onRequestError = Sentry.experimental_captureRequestError; +export const onRequestError = Sentry.captureRequestError; diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/instrumentation.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/instrumentation.ts index cd269ab160e7..a95bb9ee95ee 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/instrumentation.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/instrumentation.ts @@ -15,3 +15,5 @@ export function register() { }); } } + +export const onRequestError = Sentry.captureRequestError; diff --git a/packages/nextjs/src/common/captureRequestError.ts b/packages/nextjs/src/common/captureRequestError.ts index 8350a0f2e593..1556076619a0 100644 --- a/packages/nextjs/src/common/captureRequestError.ts +++ b/packages/nextjs/src/common/captureRequestError.ts @@ -13,17 +13,9 @@ type ErrorContext = { }; /** - * Reports errors for the Next.js `onRequestError` instrumentation hook. - * - * Notice: This function is experimental and not intended for production use. Breaking changes may be done to this funtion in any release. - * - * @experimental + * Reports errors passed to the the Next.js `onRequestError` instrumentation hook. */ -export function experimental_captureRequestError( - error: unknown, - request: RequestInfo, - errorContext: ErrorContext, -): void { +export function captureRequestError(error: unknown, request: RequestInfo, errorContext: ErrorContext): void { withScope(scope => { scope.setSDKProcessingMetadata({ request: { @@ -48,3 +40,11 @@ export function experimental_captureRequestError( }); }); } + +/** + * Reports errors passed to the the Next.js `onRequestError` instrumentation hook. + * + * @deprecated Use `captureRequestError` instead. + */ +// TODO(v9): Remove this export +export const experimental_captureRequestError = captureRequestError; diff --git a/packages/nextjs/src/common/index.ts b/packages/nextjs/src/common/index.ts index 23ddfa383772..354113637a30 100644 --- a/packages/nextjs/src/common/index.ts +++ b/packages/nextjs/src/common/index.ts @@ -11,4 +11,5 @@ export { wrapMiddlewareWithSentry } from './wrapMiddlewareWithSentry'; export { wrapPageComponentWithSentry } from './wrapPageComponentWithSentry'; export { wrapGenerationFunctionWithSentry } from './wrapGenerationFunctionWithSentry'; export { withServerActionInstrumentation } from './withServerActionInstrumentation'; -export { experimental_captureRequestError } from './captureRequestError'; +// eslint-disable-next-line deprecation/deprecation +export { experimental_captureRequestError, captureRequestError } from './captureRequestError'; diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts index b093968bdebe..a272990162b3 100644 --- a/packages/nextjs/src/index.types.ts +++ b/packages/nextjs/src/index.types.ts @@ -141,4 +141,5 @@ export declare function wrapApiHandlerWithSentryVercelCrons(WrappingTarget: C): C; -export { experimental_captureRequestError } from './common/captureRequestError'; +// eslint-disable-next-line deprecation/deprecation +export { experimental_captureRequestError, captureRequestError } from './common/captureRequestError'; From 86aeba871befc7ebbdbde90a762c936c0c549411 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Mon, 2 Sep 2024 15:56:43 +0200 Subject: [PATCH 44/64] feat(nestjs): Add `SentryGlobalGraphQLFilter` (#13545) `BaseExceptionFilter` should not be used in GraphQL applications ([ref](https://github.com/nestjs/nest/issues/5958#issuecomment-747483125)). Currently the `SentryGlobalFilter` extends the `BaseExceptionFilter`, which is why GraphQL applications break if an exception is thrown. By default, NestJS + GraphQL environments use the `ExternalExceptionFilter` ([ref](https://github.com/nestjs/nest/blob/master/packages/core/exceptions/external-exception-filter.ts)), which essentially just rethrows the error. So adding a new `SentryGlobalGraphQLFilter` that captures the exception in Sentry and then simply rethrows the error. Additionally, added a new e2e test app to test GraphQL applications. Added a test to verify that basic error reporting works correctly now. --- .github/workflows/build.yml | 1 + .../nestjs-graphql/.gitignore | 56 ++++++++++++++++++ .../test-applications/nestjs-graphql/.npmrc | 2 + .../nestjs-graphql/nest-cli.json | 8 +++ .../nestjs-graphql/package.json | 50 ++++++++++++++++ .../nestjs-graphql/playwright.config.mjs | 7 +++ .../nestjs-graphql/src/app.module.ts | 30 ++++++++++ .../nestjs-graphql/src/app.resolver.ts | 14 +++++ .../nestjs-graphql/src/instrument.ts | 8 +++ .../nestjs-graphql/src/main.ts | 15 +++++ .../nestjs-graphql/start-event-proxy.mjs | 6 ++ .../nestjs-graphql/tests/errors.test.ts | 49 ++++++++++++++++ .../nestjs-graphql/tsconfig.build.json | 4 ++ .../nestjs-graphql/tsconfig.json | 22 +++++++ packages/nestjs/src/setup.ts | 57 +++++++++++++++++-- 15 files changed, 325 insertions(+), 4 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/nest-cli.json create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/package.json create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/playwright.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/src/app.module.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/src/app.resolver.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/src/instrument.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/src/main.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/tests/errors.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/tsconfig.build.json create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-graphql/tsconfig.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cc43c68c0385..539f2537fc78 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -908,6 +908,7 @@ jobs: 'nestjs-basic', 'nestjs-distributed-tracing', 'nestjs-with-submodules', + 'nestjs-graphql', 'node-exports-test-app', 'node-koa', 'node-connect', diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/.gitignore b/dev-packages/e2e-tests/test-applications/nestjs-graphql/.gitignore new file mode 100644 index 000000000000..4b56acfbebf4 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/.gitignore @@ -0,0 +1,56 @@ +# compiled output +/dist +/node_modules +/build + +# Logs +logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# temp directory +.temp +.tmp + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-graphql/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/nest-cli.json b/dev-packages/e2e-tests/test-applications/nestjs-graphql/nest-cli.json new file mode 100644 index 000000000000..f9aa683b1ad5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/package.json b/dev-packages/e2e-tests/test-applications/nestjs-graphql/package.json new file mode 100644 index 000000000000..7981c64e0b2a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/package.json @@ -0,0 +1,50 @@ +{ + "name": "nestjs-graphql", + "version": "0.0.1", + "private": true, + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "clean": "npx rimraf node_modules pnpm-lock.yaml", + "test": "playwright test", + "test:build": "pnpm install", + "test:assert": "pnpm test" + }, + "dependencies": { + "@apollo/server": "^4.10.4", + "@nestjs/apollo": "^12.2.0", + "@nestjs/common": "^10.3.10", + "@nestjs/core": "^10.3.10", + "@nestjs/graphql": "^12.2.0", + "@nestjs/platform-express": "^10.3.10", + "@sentry/nestjs": "^8.21.0", + "graphql": "^16.9.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@playwright/test": "^1.44.1", + "@sentry-internal/test-utils": "link:../../../test-utils", + "@nestjs/cli": "^10.0.0", + "@nestjs/schematics": "^10.0.0", + "@nestjs/testing": "^10.0.0", + "@types/express": "^4.17.17", + "@types/node": "18.15.1", + "@types/supertest": "^6.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.42.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "prettier": "^3.0.0", + "source-map-support": "^0.5.21", + "supertest": "^6.3.3", + "ts-loader": "^9.4.3", + "tsconfig-paths": "^4.2.0", + "typescript": "^4.9.5" + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/nestjs-graphql/playwright.config.mjs new file mode 100644 index 000000000000..31f2b913b58b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/playwright.config.mjs @@ -0,0 +1,7 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig({ + startCommand: `pnpm start`, +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/src/app.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-graphql/src/app.module.ts new file mode 100644 index 000000000000..4cfc2ebd33e4 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/src/app.module.ts @@ -0,0 +1,30 @@ +import { ApolloDriver } from '@nestjs/apollo'; +import { Logger, Module } from '@nestjs/common'; +import { APP_FILTER } from '@nestjs/core'; +import { GraphQLModule } from '@nestjs/graphql'; +import { SentryGlobalGraphQLFilter, SentryModule } from '@sentry/nestjs/setup'; +import { AppResolver } from './app.resolver'; + +@Module({ + imports: [ + SentryModule.forRoot(), + GraphQLModule.forRoot({ + driver: ApolloDriver, + autoSchemaFile: true, + playground: true, // sets up a playground on https://localhost:3000/graphql + }), + ], + controllers: [], + providers: [ + AppResolver, + { + provide: APP_FILTER, + useClass: SentryGlobalGraphQLFilter, + }, + { + provide: Logger, + useClass: Logger, + }, + ], +}) +export class AppModule {} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/src/app.resolver.ts b/dev-packages/e2e-tests/test-applications/nestjs-graphql/src/app.resolver.ts new file mode 100644 index 000000000000..0e4dfc643918 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/src/app.resolver.ts @@ -0,0 +1,14 @@ +import { Query, Resolver } from '@nestjs/graphql'; + +@Resolver() +export class AppResolver { + @Query(() => String) + test(): string { + return 'Test endpoint!'; + } + + @Query(() => String) + error(): string { + throw new Error('This is an exception!'); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/src/instrument.ts b/dev-packages/e2e-tests/test-applications/nestjs-graphql/src/instrument.ts new file mode 100644 index 000000000000..f1f4de865435 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/src/instrument.ts @@ -0,0 +1,8 @@ +import * as Sentry from '@sentry/nestjs'; + +Sentry.init({ + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: process.env.E2E_TEST_DSN, + tunnel: `http://localhost:3031/`, // proxy server + tracesSampleRate: 1, +}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/src/main.ts b/dev-packages/e2e-tests/test-applications/nestjs-graphql/src/main.ts new file mode 100644 index 000000000000..71ce685f4d61 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/src/main.ts @@ -0,0 +1,15 @@ +// Import this first +import './instrument'; + +// Import other modules +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; + +const PORT = 3030; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + await app.listen(PORT); +} + +bootstrap(); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/nestjs-graphql/start-event-proxy.mjs new file mode 100644 index 000000000000..62fff27d8500 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'nestjs-graphql', +}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-graphql/tests/errors.test.ts new file mode 100644 index 000000000000..48e0ef8c2c9f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/tests/errors.test.ts @@ -0,0 +1,49 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; + +test('Sends exception to Sentry', async ({ baseURL }) => { + const errorEventPromise = waitForError('nestjs-graphql', event => { + return !event.type && event.exception?.values?.[0]?.value === 'This is an exception!'; + }); + + const response = await fetch(`${baseURL}/graphql`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: `query { error }`, + }), + }); + + const json_response = await response.json(); + const errorEvent = await errorEventPromise; + + expect(json_response?.errors[0]).toEqual({ + message: 'This is an exception!', + locations: expect.any(Array), + path: ['error'], + extensions: { + code: 'INTERNAL_SERVER_ERROR', + stacktrace: expect.any(Array), + }, + }); + + expect(errorEvent.exception?.values).toHaveLength(1); + expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception!'); + + expect(errorEvent.request).toEqual({ + method: 'POST', + cookies: {}, + data: '{"query":"query { error }"}', + headers: expect.any(Object), + url: 'http://localhost:3030/graphql', + }); + + expect(errorEvent.transaction).toEqual('POST /graphql'); + + expect(errorEvent.contexts?.trace).toEqual({ + trace_id: expect.any(String), + span_id: expect.any(String), + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/tsconfig.build.json b/dev-packages/e2e-tests/test-applications/nestjs-graphql/tsconfig.build.json new file mode 100644 index 000000000000..26c30d4eddf2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist"] +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-graphql/tsconfig.json b/dev-packages/e2e-tests/test-applications/nestjs-graphql/tsconfig.json new file mode 100644 index 000000000000..cf79f029c781 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-graphql/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2021", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false, + "strictBindCallApply": false, + "forceConsistentCasingInFileNames": false, + "noFallthroughCasesInSwitch": false, + "moduleResolution": "Node16" + } +} diff --git a/packages/nestjs/src/setup.ts b/packages/nestjs/src/setup.ts index f284c4ed7875..88d58ffea22f 100644 --- a/packages/nestjs/src/setup.ts +++ b/packages/nestjs/src/setup.ts @@ -6,7 +6,7 @@ import type { NestInterceptor, OnModuleInit, } from '@nestjs/common'; -import { Catch, Global, Injectable, Module } from '@nestjs/common'; +import { Catch, Global, HttpException, Injectable, Logger, Module } from '@nestjs/common'; import { APP_INTERCEPTOR, BaseExceptionFilter } from '@nestjs/core'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP, @@ -31,7 +31,11 @@ import { isExpectedError } from './helpers'; */ class SentryTracingInterceptor implements NestInterceptor { // used to exclude this class from being auto-instrumented - public static readonly __SENTRY_INTERNAL__ = true; + public readonly __SENTRY_INTERNAL__: boolean; + + public constructor() { + this.__SENTRY_INTERNAL__ = true; + } /** * Intercepts HTTP requests to set the transaction name for Sentry tracing. @@ -61,7 +65,12 @@ export { SentryTracingInterceptor }; * Global filter to handle exceptions and report them to Sentry. */ class SentryGlobalFilter extends BaseExceptionFilter { - public static readonly __SENTRY_INTERNAL__ = true; + public readonly __SENTRY_INTERNAL__: boolean; + + public constructor() { + super(); + this.__SENTRY_INTERNAL__ = true; + } /** * Catches exceptions and reports them to Sentry unless they are expected errors. @@ -78,11 +87,51 @@ class SentryGlobalFilter extends BaseExceptionFilter { Catch()(SentryGlobalFilter); export { SentryGlobalFilter }; +/** + * Global filter to handle exceptions and report them to Sentry. + * + * The BaseExceptionFilter does not work well in GraphQL applications. + * By default, Nest GraphQL applications use the ExternalExceptionFilter, which just rethrows the error: + * https://github.com/nestjs/nest/blob/master/packages/core/exceptions/external-exception-filter.ts + * + * The ExternalExceptinFilter is not exported, so we reimplement this filter here. + */ +class SentryGlobalGraphQLFilter { + private static readonly _logger = new Logger('ExceptionsHandler'); + public readonly __SENTRY_INTERNAL__: boolean; + + public constructor() { + this.__SENTRY_INTERNAL__ = true; + } + + /** + * Catches exceptions and reports them to Sentry unless they are HttpExceptions. + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public catch(exception: unknown, host: ArgumentsHost): void { + // neither report nor log HttpExceptions + if (exception instanceof HttpException) { + throw exception; + } + if (exception instanceof Error) { + SentryGlobalGraphQLFilter._logger.error(exception.message, exception.stack); + } + captureException(exception); + throw exception; + } +} +Catch()(SentryGlobalGraphQLFilter); +export { SentryGlobalGraphQLFilter }; + /** * Service to set up Sentry performance tracing for Nest.js applications. */ class SentryService implements OnModuleInit { - public static readonly __SENTRY_INTERNAL__ = true; + public readonly __SENTRY_INTERNAL__: boolean; + + public constructor() { + this.__SENTRY_INTERNAL__ = true; + } /** * Initializes the Sentry service and registers span attributes. From 00a45a4cc93af28a790dd66ebcd98bb1cd510dc4 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Mon, 2 Sep 2024 16:02:48 +0200 Subject: [PATCH 45/64] fix(nestjs): Check arguments before instrumenting with `@Injectable` (#13544) Before this fix, calling a `use` method on a service that does not implement the `NestMiddleware` interface resulted in an error. This is because we try to proxy the third argument to the function, which in middleware is an express `NextFunction`, but in other classes can be anything, potentially even undefined. This fix introduces further guards to early-return in non-middleware targets. Added a test to verify that services with `use` work fine now. Also added additional tests to verify that this behavior does not occur for `canActivate` (guard), `intercept` (interceptor) and `transform` (pipe) methods. --- .../nestjs-basic/src/app.controller.ts | 20 ++++++++ .../nestjs-basic/src/app.service.ts | 16 ++++++ .../nestjs-basic/tests/transactions.test.ts | 20 ++++++++ .../node-nestjs-basic/src/app.controller.ts | 20 ++++++++ .../node-nestjs-basic/src/app.service.ts | 16 ++++++ .../tests/transactions.test.ts | 20 ++++++++ .../src/integrations/tracing/nest/helpers.ts | 21 +++++++- .../nest/sentry-nest-instrumentation.ts | 51 +++++++++++++------ .../src/integrations/tracing/nest/types.ts | 5 ++ 9 files changed, 173 insertions(+), 16 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts index 1649cdb94b5f..75308e8f0ea9 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts @@ -96,4 +96,24 @@ export class AppController { async exampleExceptionLocalFilter() { throw new ExampleExceptionLocalFilter(); } + + @Get('test-service-use') + testServiceWithUseMethod() { + return this.appService.use(); + } + + @Get('test-service-transform') + testServiceWithTransform() { + return this.appService.transform(); + } + + @Get('test-service-intercept') + testServiceWithIntercept() { + return this.appService.intercept(); + } + + @Get('test-service-canActivate') + testServiceWithCanActivate() { + return this.appService.canActivate(); + } } diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.service.ts index f1c935257013..3e4639040a7e 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.service.ts @@ -78,4 +78,20 @@ export class AppService { async killTestCron() { this.schedulerRegistry.deleteCronJob('test-cron-job'); } + + use() { + console.log('Test use!'); + } + + transform() { + console.log('Test transform!'); + } + + intercept() { + console.log('Test intercept!'); + } + + canActivate() { + console.log('Test canActivate!'); + } } diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts index e33e63f319ca..a9c8ffea9117 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts @@ -707,3 +707,23 @@ test('API route transaction includes exactly one nest async interceptor span aft // 'Interceptor - After Route' is NOT the parent of 'test-controller-span' expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptorSpanAfterRouteId); }); + +test('Calling use method on service with Injectable decorator returns 200', async ({ baseURL }) => { + const response = await fetch(`${baseURL}/test-service-use`); + expect(response.status).toBe(200); +}); + +test('Calling transform method on service with Injectable decorator returns 200', async ({ baseURL }) => { + const response = await fetch(`${baseURL}/test-service-transform`); + expect(response.status).toBe(200); +}); + +test('Calling intercept method on service with Injectable decorator returns 200', async ({ baseURL }) => { + const response = await fetch(`${baseURL}/test-service-intercept`); + expect(response.status).toBe(200); +}); + +test('Calling canActivate method on service with Injectable decorator returns 200', async ({ baseURL }) => { + const response = await fetch(`${baseURL}/test-service-canActivate`); + expect(response.status).toBe(200); +}); diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.controller.ts index 1f141dc0f966..70c734c61d73 100644 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.controller.ts +++ b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.controller.ts @@ -82,4 +82,24 @@ export class AppController { async flush() { await flush(); } + + @Get('test-service-use') + testServiceWithUseMethod() { + return this.appService.use(); + } + + @Get('test-service-transform') + testServiceWithTransform() { + return this.appService.transform(); + } + + @Get('test-service-intercept') + testServiceWithIntercept() { + return this.appService.intercept(); + } + + @Get('test-service-canActivate') + testServiceWithCanActivate() { + return this.appService.canActivate(); + } } diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.service.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.service.ts index f1c935257013..3e4639040a7e 100644 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.service.ts @@ -78,4 +78,20 @@ export class AppService { async killTestCron() { this.schedulerRegistry.deleteCronJob('test-cron-job'); } + + use() { + console.log('Test use!'); + } + + transform() { + console.log('Test transform!'); + } + + intercept() { + console.log('Test intercept!'); + } + + canActivate() { + console.log('Test canActivate!'); + } } diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/transactions.test.ts index eb52dfe98585..23855d1f55b8 100644 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/transactions.test.ts @@ -705,3 +705,23 @@ test('API route transaction includes exactly one nest async interceptor span aft // 'Interceptor - After Route' is NOT the parent of 'test-controller-span' expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptorSpanAfterRouteId); }); + +test('Calling use method on service with Injectable decorator returns 200', async ({ baseURL }) => { + const response = await fetch(`${baseURL}/test-service-use`); + expect(response.status).toBe(200); +}); + +test('Calling transform method on service with Injectable decorator returns 200', async ({ baseURL }) => { + const response = await fetch(`${baseURL}/test-service-transform`); + expect(response.status).toBe(200); +}); + +test('Calling intercept method on service with Injectable decorator returns 200', async ({ baseURL }) => { + const response = await fetch(`${baseURL}/test-service-intercept`); + expect(response.status).toBe(200); +}); + +test('Calling canActivate method on service with Injectable decorator returns 200', async ({ baseURL }) => { + const response = await fetch(`${baseURL}/test-service-canActivate`); + expect(response.status).toBe(200); +}); diff --git a/packages/node/src/integrations/tracing/nest/helpers.ts b/packages/node/src/integrations/tracing/nest/helpers.ts index e2d3c4c573b9..cc83dda3855d 100644 --- a/packages/node/src/integrations/tracing/nest/helpers.ts +++ b/packages/node/src/integrations/tracing/nest/helpers.ts @@ -1,7 +1,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, withActiveSpan } from '@sentry/core'; import type { Span } from '@sentry/types'; import { addNonEnumerableProperty } from '@sentry/utils'; -import type { CatchTarget, InjectableTarget, Observable, Subscription } from './types'; +import type { CatchTarget, InjectableTarget, NextFunction, Observable, Subscription } from './types'; const sentryPatched = 'sentryPatched'; @@ -53,3 +53,22 @@ export function instrumentObservable(observable: Observable, activeSpan }); } } + +/** + * Proxies the next() call in a nestjs middleware to end the span when it is called. + */ +export function getNextProxy(next: NextFunction, span: Span, prevSpan: undefined | Span): NextFunction { + return new Proxy(next, { + apply: (originalNext, thisArgNext, argsNext) => { + span.end(); + + if (prevSpan) { + return withActiveSpan(prevSpan, () => { + return Reflect.apply(originalNext, thisArgNext, argsNext); + }); + } else { + return Reflect.apply(originalNext, thisArgNext, argsNext); + } + }, + }); +} diff --git a/packages/node/src/integrations/tracing/nest/sentry-nest-instrumentation.ts b/packages/node/src/integrations/tracing/nest/sentry-nest-instrumentation.ts index b8ea782ee66f..2d59d97d87fd 100644 --- a/packages/node/src/integrations/tracing/nest/sentry-nest-instrumentation.ts +++ b/packages/node/src/integrations/tracing/nest/sentry-nest-instrumentation.ts @@ -8,7 +8,7 @@ import { import { getActiveSpan, startInactiveSpan, startSpan, startSpanManual, withActiveSpan } from '@sentry/core'; import type { Span } from '@sentry/types'; import { SDK_VERSION, addNonEnumerableProperty, isThenable } from '@sentry/utils'; -import { getMiddlewareSpanOptions, instrumentObservable, isPatched } from './helpers'; +import { getMiddlewareSpanOptions, getNextProxy, instrumentObservable, isPatched } from './helpers'; import type { CallHandler, CatchTarget, InjectableTarget, MinimalNestJsExecutionContext, Observable } from './types'; const supportedVersions = ['>=8.0.0 <11']; @@ -101,23 +101,19 @@ export class SentryNestInstrumentation extends InstrumentationBase { target.prototype.use = new Proxy(target.prototype.use, { apply: (originalUse, thisArgUse, argsUse) => { const [req, res, next, ...args] = argsUse; - const prevSpan = getActiveSpan(); - return startSpanManual(getMiddlewareSpanOptions(target), (span: Span) => { - const nextProxy = new Proxy(next, { - apply: (originalNext, thisArgNext, argsNext) => { - span.end(); + // Check that we can reasonably assume that the target is a middleware. + // Without these guards, instrumentation will fail if a function named 'use' on a service, which is + // decorated with @Injectable, is called. + if (!req || !res || !next || typeof next !== 'function') { + return originalUse.apply(thisArgUse, argsUse); + } - if (prevSpan) { - return withActiveSpan(prevSpan, () => { - return Reflect.apply(originalNext, thisArgNext, argsNext); - }); - } else { - return Reflect.apply(originalNext, thisArgNext, argsNext); - } - }, - }); + const prevSpan = getActiveSpan(); + return startSpanManual(getMiddlewareSpanOptions(target), (span: Span) => { + // proxy next to end span on call + const nextProxy = getNextProxy(next, span, prevSpan); return originalUse.apply(thisArgUse, [req, res, nextProxy, args]); }); }, @@ -133,6 +129,12 @@ export class SentryNestInstrumentation extends InstrumentationBase { target.prototype.canActivate = new Proxy(target.prototype.canActivate, { apply: (originalCanActivate, thisArgCanActivate, argsCanActivate) => { + const context: MinimalNestJsExecutionContext = argsCanActivate[0]; + + if (!context) { + return originalCanActivate.apply(thisArgCanActivate, argsCanActivate); + } + return startSpan(getMiddlewareSpanOptions(target), () => { return originalCanActivate.apply(thisArgCanActivate, argsCanActivate); }); @@ -148,6 +150,13 @@ export class SentryNestInstrumentation extends InstrumentationBase { target.prototype.transform = new Proxy(target.prototype.transform, { apply: (originalTransform, thisArgTransform, argsTransform) => { + const value = argsTransform[0]; + const metadata = argsTransform[1]; + + if (!value || !metadata) { + return originalTransform.apply(thisArgTransform, argsTransform); + } + return startSpan(getMiddlewareSpanOptions(target), () => { return originalTransform.apply(thisArgTransform, argsTransform); }); @@ -169,6 +178,11 @@ export class SentryNestInstrumentation extends InstrumentationBase { const parentSpan = getActiveSpan(); let afterSpan: Span; + // Check that we can reasonably assume that the target is an interceptor. + if (!context || !next || typeof next.handle !== 'function') { + return originalIntercept.apply(thisArgIntercept, argsIntercept); + } + return startSpanManual(getMiddlewareSpanOptions(target), (beforeSpan: Span) => { // eslint-disable-next-line @typescript-eslint/unbound-method next.handle = new Proxy(next.handle, { @@ -263,6 +277,13 @@ export class SentryNestInstrumentation extends InstrumentationBase { target.prototype.catch = new Proxy(target.prototype.catch, { apply: (originalCatch, thisArgCatch, argsCatch) => { + const exception = argsCatch[0]; + const host = argsCatch[1]; + + if (!exception || !host) { + return originalCatch.apply(thisArgCatch, argsCatch); + } + return startSpan(getMiddlewareSpanOptions(target), () => { return originalCatch.apply(thisArgCatch, argsCatch); }); diff --git a/packages/node/src/integrations/tracing/nest/types.ts b/packages/node/src/integrations/tracing/nest/types.ts index 18bd26b31ccb..0590462c09d5 100644 --- a/packages/node/src/integrations/tracing/nest/types.ts +++ b/packages/node/src/integrations/tracing/nest/types.ts @@ -73,3 +73,8 @@ export interface CatchTarget { catch?: (...args: any[]) => any; }; } + +/** + * Represents an express NextFunction. + */ +export type NextFunction = (err?: any) => void; From 83864fa5361e19266fd3fc102ad19561ba53656d Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Mon, 2 Sep 2024 16:09:03 +0200 Subject: [PATCH 46/64] chore(nestjs): Promote NestJS SDK to beta state (Readme) (#13521) --- packages/nestjs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nestjs/README.md b/packages/nestjs/README.md index a78a2c45a620..95611c9a7370 100644 --- a/packages/nestjs/README.md +++ b/packages/nestjs/README.md @@ -4,13 +4,13 @@

-# Official Sentry SDK for NestJS (EXPERIMENTAL) +# Official Sentry SDK for NestJS [![npm version](https://img.shields.io/npm/v/@sentry/nestjs.svg)](https://www.npmjs.com/package/@sentry/nestjs) [![npm dm](https://img.shields.io/npm/dm/@sentry/nestjs.svg)](https://www.npmjs.com/package/@sentry/nestjs) [![npm dt](https://img.shields.io/npm/dt/@sentry/nestjs.svg)](https://www.npmjs.com/package/@sentry/nestjs) -This SDK is considered **experimental and in an alpha state**. It may experience breaking changes. Please reach out on +This SDK is in **Beta**. The API is stable but updates may include minor changes in behavior. Please reach out on [GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have any feedback or concerns. ## Installation From a8ff09fcbdaaac13637e62b3a3bd755676617223 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Mon, 2 Sep 2024 16:31:46 +0200 Subject: [PATCH 47/64] chore(nestjs): Add note about `SentryGlobalGraphQLFilter` in nestjs README (#13554) Forgot to update the README. --- packages/nestjs/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nestjs/README.md b/packages/nestjs/README.md index 95611c9a7370..0cdb832a75f6 100644 --- a/packages/nestjs/README.md +++ b/packages/nestjs/README.md @@ -109,6 +109,8 @@ import { SentryGlobalFilter } from '@sentry/nestjs/setup'; export class AppModule {} ``` +**Note:** In NestJS + GraphQL applications replace the `SentryGlobalFilter` with the `SentryGlobalGraphQLFilter`. + ## SentryTraced Use the `@SentryTraced()` decorator to gain additional performance insights for any function within your NestJS From c9c346c88fab570223d2c7070ba81dc070229275 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 17:17:15 +0200 Subject: [PATCH 48/64] ref: Add external contributor to CHANGELOG.md (#13552) This PR adds the external contributor to the CHANGELOG.md file, so that they are credited for their contribution. See #13540 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 090365e69352..da2ba89164fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott -Work in this release was contributed by @leopoldkristjansson. Thank you for your contribution! +Work in this release was contributed by @leopoldkristjansson and @mhuggins. Thank you for your contributions! ### Important Changes From 56054016bf1f02b6db5992f01d7247f626dc249a Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Mon, 2 Sep 2024 17:54:42 +0200 Subject: [PATCH 49/64] meta: Update CHANGELOG for 8.28.0 --- CHANGELOG.md | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da2ba89164fe..df23b70b847b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,16 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott -Work in this release was contributed by @leopoldkristjansson and @mhuggins. Thank you for your contributions! +## 8.28.0 ### Important Changes +- **Beta release of official NestJS SDK** + +This release contains the beta version of `@sentry/nestjs`! For details on how to use it, check out the +[README](https://github.com/getsentry/sentry-javascript/blob/master/packages/nestjs/README.md). Any feedback/bug reports +are greatly appreciated, please reach out on GitHub. + - **fix(browser): Remove faulty LCP, FCP and FP normalization logic (#13502)** This release fixes a bug in the `@sentry/browser` package and all SDKs depending on this package (e.g. `@sentry/react` @@ -23,7 +29,25 @@ this version, you might experience an increase in LCP, FCP and FP values, which performance score in the Web Vitals Insights module in Sentry. This is because the previously reported values were smaller than the actually measured values. We apologize for the inconvenience! -Work in this release was contributed by @leopoldkristjansson and @filips123. Thank you for your contributions! +### Other Changes + +- feat(nestjs): Add `SentryGlobalGraphQLFilter` (#13545) +- feat(nestjs): Automatic instrumentation of nestjs interceptors after route execution (#13264) +- feat(nextjs): Add `bundleSizeOptimizations` to build options (#13323) +- feat(nextjs): Stabilize `captureRequestError` (#13550) +- feat(nuxt): Wrap config in nuxt context (#13457) +- feat(profiling): Expose profiler as top level primitive (#13512) +- feat(replay): Add layout shift to CLS replay data (#13386) +- feat(replay): Upgrade rrweb packages to 2.26.0 (#13483) +- fix(cdn): Do not mangle \_metadata (#13426) +- fix(cdn): Fix SDK source for CDN bundles (#13475) +- fix(nestjs): Check arguments before instrumenting with `@Injectable` (#13544) +- fix(node): Suppress tracing for transport request execution rather than transport creation (#13491) +- fix(replay): Consider more things as DOM mutations for dead clicks (#13518) +- fix(vue): Correctly obtain component name (#13484) + +Work in this release was contributed by @leopoldkristjansson, @mhuggins and @filips123. Thank you for your +contributions! ## 8.27.0 From 9b8f9ea30b7b7c5f6c325b58519342b0741d7667 Mon Sep 17 00:00:00 2001 From: Andrei <168741329+andreiborza@users.noreply.github.com> Date: Tue, 3 Sep 2024 09:10:15 +0200 Subject: [PATCH 50/64] fix(nestjs): Ensure exception and host are correctly passed on when using @WithSentry (#13564) Also adds the `nestjs-with-submodules-decorator` e2e test app to be run on CI. --- .github/workflows/build.yml | 1 + packages/nestjs/src/decorators/with-sentry.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 539f2537fc78..4f06228f3a7e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -908,6 +908,7 @@ jobs: 'nestjs-basic', 'nestjs-distributed-tracing', 'nestjs-with-submodules', + 'nestjs-with-submodules-decorator', 'nestjs-graphql', 'node-exports-test-app', 'node-koa', diff --git a/packages/nestjs/src/decorators/with-sentry.ts b/packages/nestjs/src/decorators/with-sentry.ts index 6b02d73a94c8..cf86ea6e7cc5 100644 --- a/packages/nestjs/src/decorators/with-sentry.ts +++ b/packages/nestjs/src/decorators/with-sentry.ts @@ -12,11 +12,11 @@ export function WithSentry() { // eslint-disable-next-line @typescript-eslint/no-explicit-any descriptor.value = function (exception: unknown, host: unknown, ...args: any[]) { if (isExpectedError(exception)) { - return originalCatch.apply(this, args); + return originalCatch.apply(this, [exception, host, ...args]); } captureException(exception); - return originalCatch.apply(this, args); + return originalCatch.apply(this, [exception, host, ...args]); }; return descriptor; From c2258fbd99095c35857b0a918daa40b16b4174d1 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Tue, 3 Sep 2024 09:15:30 +0200 Subject: [PATCH 51/64] meta: Update CHANGELOG for 8.28.0 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index df23b70b847b..015c5370a75a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ smaller than the actually measured values. We apologize for the inconvenience! - fix(cdn): Do not mangle \_metadata (#13426) - fix(cdn): Fix SDK source for CDN bundles (#13475) - fix(nestjs): Check arguments before instrumenting with `@Injectable` (#13544) +- fix(nestjs): Ensure exception and host are correctly passed on when using @WithSentry (#13564) - fix(node): Suppress tracing for transport request execution rather than transport creation (#13491) - fix(replay): Consider more things as DOM mutations for dead clicks (#13518) - fix(vue): Correctly obtain component name (#13484) From 128ab44c58da6796425a491a5e516594789bef0a Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 3 Sep 2024 10:12:46 +0200 Subject: [PATCH 52/64] test(browser): Add integration test for `httpContextIntegration` (#13553) Add a test to our browser integration tests where we explicitly check that httpContextIntegration sets all its supported properties: url, user-agent and (previously untested) referer. --- .../suites/integrations/httpContext/init.js | 20 ++++++++++++++ .../integrations/httpContext/subject.js | 1 + .../suites/integrations/httpContext/test.ts | 26 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 dev-packages/browser-integration-tests/suites/integrations/httpContext/init.js create mode 100644 dev-packages/browser-integration-tests/suites/integrations/httpContext/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpContext/init.js b/dev-packages/browser-integration-tests/suites/integrations/httpContext/init.js new file mode 100644 index 000000000000..984534454ac7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/httpContext/init.js @@ -0,0 +1,20 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +const integrations = Sentry.getDefaultIntegrations({}).filter( + defaultIntegration => defaultIntegration.name === 'HttpContext', +); + +const client = new Sentry.BrowserClient({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + transport: Sentry.makeFetchTransport, + stackParser: Sentry.defaultStackParser, + integrations: integrations, +}); + +const scope = new Sentry.Scope(); +scope.setClient(client); +client.init(); + +window._sentryScope = scope; diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpContext/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpContext/subject.js new file mode 100644 index 000000000000..62ce205e2ffc --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/httpContext/subject.js @@ -0,0 +1 @@ +window._sentryScope.captureException(new Error('client init')); diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts new file mode 100644 index 000000000000..02a62142e02b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts @@ -0,0 +1,26 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; + +sentryTest('httpContextIntegration captures user-agent and referrer', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const errorEventPromise = getFirstSentryEnvelopeRequest(page); + + // Simulate document.referrer being set to test full functionality of the integration + await page.goto(url, { referer: 'https://sentry.io/' }); + + const errorEvent = await errorEventPromise; + + expect(errorEvent.exception?.values).toHaveLength(1); + + expect(errorEvent.request).toEqual({ + headers: { + 'User-Agent': expect.any(String), + Referer: 'https://sentry.io/', + }, + url: expect.any(String), + }); +}); From e78f15e29c6a65e34f7ab92932e174b702067553 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Tue, 3 Sep 2024 08:21:17 +0000 Subject: [PATCH 53/64] release: 8.28.0 --- .../browser-integration-tests/package.json | 4 ++-- .../bundle-analyzer-scenarios/package.json | 2 +- .../clear-cache-gh-action/package.json | 2 +- dev-packages/e2e-tests/package.json | 2 +- .../package.json | 2 +- .../node-integration-tests/package.json | 10 +++++----- dev-packages/overhead-metrics/package.json | 2 +- dev-packages/rollup-utils/package.json | 2 +- dev-packages/size-limit-gh-action/package.json | 2 +- dev-packages/test-utils/package.json | 6 +++--- lerna.json | 2 +- packages/angular/package.json | 10 +++++----- packages/astro/package.json | 12 ++++++------ packages/aws-serverless/package.json | 10 +++++----- packages/browser-utils/package.json | 8 ++++---- packages/browser/package.json | 18 +++++++++--------- packages/bun/package.json | 12 ++++++------ packages/cloudflare/package.json | 8 ++++---- packages/core/package.json | 6 +++--- packages/deno/package.json | 8 ++++---- packages/ember/package.json | 10 +++++----- packages/eslint-config-sdk/package.json | 6 +++--- packages/eslint-plugin-sdk/package.json | 2 +- packages/feedback/package.json | 8 ++++---- packages/gatsby/package.json | 10 +++++----- packages/google-cloud-serverless/package.json | 10 +++++----- packages/integration-shims/package.json | 8 ++++---- packages/nestjs/package.json | 10 +++++----- packages/nextjs/package.json | 16 ++++++++-------- packages/node/package.json | 10 +++++----- packages/nuxt/package.json | 16 ++++++++-------- packages/opentelemetry/package.json | 8 ++++---- packages/profiling-node/package.json | 10 +++++----- packages/react/package.json | 10 +++++----- packages/remix/package.json | 14 +++++++------- packages/replay-canvas/package.json | 10 +++++----- packages/replay-internal/package.json | 12 ++++++------ packages/replay-worker/package.json | 2 +- packages/solid/package.json | 10 +++++----- packages/solidstart/package.json | 14 +++++++------- packages/svelte/package.json | 10 +++++----- packages/sveltekit/package.json | 14 +++++++------- packages/types/package.json | 2 +- packages/typescript/package.json | 2 +- packages/utils/package.json | 4 ++-- packages/utils/src/version.ts | 2 +- packages/vercel-edge/package.json | 8 ++++---- packages/vue/package.json | 10 +++++----- packages/wasm/package.json | 10 +++++----- 49 files changed, 193 insertions(+), 193 deletions(-) diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index 8c0ca22e0358..886f91d328fb 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-integration-tests", - "version": "8.27.0", + "version": "8.28.0", "main": "index.js", "license": "MIT", "engines": { @@ -43,7 +43,7 @@ "@babel/preset-typescript": "^7.16.7", "@playwright/test": "^1.44.1", "@sentry-internal/rrweb": "2.11.0", - "@sentry/browser": "8.27.0", + "@sentry/browser": "8.28.0", "axios": "1.6.7", "babel-loader": "^8.2.2", "html-webpack-plugin": "^5.5.0", diff --git a/dev-packages/bundle-analyzer-scenarios/package.json b/dev-packages/bundle-analyzer-scenarios/package.json index 2a785b9face2..6cec8aa83b2f 100644 --- a/dev-packages/bundle-analyzer-scenarios/package.json +++ b/dev-packages/bundle-analyzer-scenarios/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/bundle-analyzer-scenarios", - "version": "8.27.0", + "version": "8.28.0", "description": "Scenarios to test bundle analysis with", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/dev-packages/bundle-analyzer-scenarios", diff --git a/dev-packages/clear-cache-gh-action/package.json b/dev-packages/clear-cache-gh-action/package.json index 492f4fc2b31e..60aa626f0dde 100644 --- a/dev-packages/clear-cache-gh-action/package.json +++ b/dev-packages/clear-cache-gh-action/package.json @@ -1,7 +1,7 @@ { "name": "@sentry-internal/clear-cache-gh-action", "description": "An internal Github Action to clear GitHub caches.", - "version": "8.26.0", + "version": "8.28.0", "license": "MIT", "engines": { "node": ">=18" diff --git a/dev-packages/e2e-tests/package.json b/dev-packages/e2e-tests/package.json index 687dac45a0c8..61f859eab5c2 100644 --- a/dev-packages/e2e-tests/package.json +++ b/dev-packages/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/e2e-tests", - "version": "8.27.0", + "version": "8.28.0", "license": "MIT", "private": true, "scripts": { diff --git a/dev-packages/external-contributor-gh-action/package.json b/dev-packages/external-contributor-gh-action/package.json index c4cdf891b886..6204fcab3e84 100644 --- a/dev-packages/external-contributor-gh-action/package.json +++ b/dev-packages/external-contributor-gh-action/package.json @@ -1,7 +1,7 @@ { "name": "@sentry-internal/external-contributor-gh-action", "description": "An internal Github Action to add external contributors to the CHANGELOG.md file.", - "version": "8.27.0", + "version": "8.28.0", "license": "MIT", "engines": { "node": ">=18" diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index fc8577a06ca0..9243a1512884 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/node-integration-tests", - "version": "8.27.0", + "version": "8.28.0", "license": "MIT", "engines": { "node": ">=14.18" @@ -31,10 +31,10 @@ "@nestjs/core": "^10.3.3", "@nestjs/platform-express": "^10.3.3", "@prisma/client": "5.9.1", - "@sentry/aws-serverless": "8.27.0", - "@sentry/node": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/aws-serverless": "8.28.0", + "@sentry/node": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "@types/mongodb": "^3.6.20", "@types/mysql": "^2.15.21", "@types/pg": "^8.6.5", diff --git a/dev-packages/overhead-metrics/package.json b/dev-packages/overhead-metrics/package.json index 3b24ab301e7d..af30273fd41b 100644 --- a/dev-packages/overhead-metrics/package.json +++ b/dev-packages/overhead-metrics/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "8.27.0", + "version": "8.28.0", "name": "@sentry-internal/overhead-metrics", "main": "index.js", "author": "Sentry", diff --git a/dev-packages/rollup-utils/package.json b/dev-packages/rollup-utils/package.json index 68f7a42bc41b..5606d15799e6 100644 --- a/dev-packages/rollup-utils/package.json +++ b/dev-packages/rollup-utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/rollup-utils", - "version": "8.27.0", + "version": "8.28.0", "description": "Rollup utilities used at Sentry for the Sentry JavaScript SDK", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/rollup-utils", diff --git a/dev-packages/size-limit-gh-action/package.json b/dev-packages/size-limit-gh-action/package.json index b383a4b302e7..a2d942ff30e6 100644 --- a/dev-packages/size-limit-gh-action/package.json +++ b/dev-packages/size-limit-gh-action/package.json @@ -1,7 +1,7 @@ { "name": "@sentry-internal/size-limit-gh-action", "description": "An internal Github Action to compare the current size of a PR against the one on develop.", - "version": "8.27.0", + "version": "8.28.0", "license": "MIT", "engines": { "node": ">=18" diff --git a/dev-packages/test-utils/package.json b/dev-packages/test-utils/package.json index 9db6d9bd38b5..10df04020be0 100644 --- a/dev-packages/test-utils/package.json +++ b/dev-packages/test-utils/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "8.27.0", + "version": "8.28.0", "name": "@sentry-internal/test-utils", "author": "Sentry", "license": "MIT", @@ -45,8 +45,8 @@ }, "devDependencies": { "@playwright/test": "^1.44.1", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "volta": { "extends": "../../package.json" diff --git a/lerna.json b/lerna.json index d36af7896191..dba33b299b7f 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "8.27.0", + "version": "8.28.0", "npmClient": "yarn" } diff --git a/packages/angular/package.json b/packages/angular/package.json index 10a61dbeaeb1..076c5d08bbf4 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/angular", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Angular", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/angular", @@ -21,10 +21,10 @@ "rxjs": "^6.5.5 || ^7.x" }, "dependencies": { - "@sentry/browser": "8.27.0", - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/browser": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "tslib": "^2.4.1" }, "devDependencies": { diff --git a/packages/astro/package.json b/packages/astro/package.json index 82c73d8a5a0a..77f9c17cb8db 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/astro", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Astro", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/astro", @@ -56,11 +56,11 @@ "astro": ">=3.x || >=4.0.0-beta" }, "dependencies": { - "@sentry/browser": "8.27.0", - "@sentry/core": "8.27.0", - "@sentry/node": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/browser": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/node": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "@sentry/vite-plugin": "^2.22.3" }, "devDependencies": { diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index 418e2a64a56a..fa20bebe24ff 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/aws-serverless", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for AWS Lambda and AWS Serverless Environments", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/serverless", @@ -66,10 +66,10 @@ "dependencies": { "@opentelemetry/instrumentation-aws-lambda": "0.43.0", "@opentelemetry/instrumentation-aws-sdk": "0.43.1", - "@sentry/core": "8.27.0", - "@sentry/node": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/core": "8.28.0", + "@sentry/node": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "@types/aws-lambda": "^8.10.62" }, "devDependencies": { diff --git a/packages/browser-utils/package.json b/packages/browser-utils/package.json index 5c6a0ba6ba00..f2aba350da39 100644 --- a/packages/browser-utils/package.json +++ b/packages/browser-utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-utils", - "version": "8.27.0", + "version": "8.28.0", "description": "Browser Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser-utils", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/browser/package.json b/packages/browser/package.json index 736a3a0349a5..e85d3da99aa6 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/browser", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for browsers", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/browser", @@ -39,16 +39,16 @@ "access": "public" }, "dependencies": { - "@sentry-internal/browser-utils": "8.27.0", - "@sentry-internal/feedback": "8.27.0", - "@sentry-internal/replay": "8.27.0", - "@sentry-internal/replay-canvas": "8.27.0", - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry-internal/browser-utils": "8.28.0", + "@sentry-internal/feedback": "8.28.0", + "@sentry-internal/replay": "8.28.0", + "@sentry-internal/replay-canvas": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "devDependencies": { - "@sentry-internal/integration-shims": "8.27.0", + "@sentry-internal/integration-shims": "8.28.0", "fake-indexeddb": "^4.0.1" }, "scripts": { diff --git a/packages/bun/package.json b/packages/bun/package.json index 3c730acc137f..af78c308caa2 100644 --- a/packages/bun/package.json +++ b/packages/bun/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/bun", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for bun", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/bun", @@ -39,11 +39,11 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/node": "8.27.0", - "@sentry/opentelemetry": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/core": "8.28.0", + "@sentry/node": "8.28.0", + "@sentry/opentelemetry": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "devDependencies": { "bun-types": "latest" diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json index ed5ca89b8094..0e4f10ca706e 100644 --- a/packages/cloudflare/package.json +++ b/packages/cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/cloudflare", - "version": "8.27.0", + "version": "8.28.0", "description": "Offical Sentry SDK for Cloudflare Workers and Pages", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/cloudflare", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "optionalDependencies": { "@cloudflare/workers-types": "^4.x" diff --git a/packages/core/package.json b/packages/core/package.json index a9bd50bc0fba..da87626676a8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/core", - "version": "8.27.0", + "version": "8.28.0", "description": "Base implementation for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/core", @@ -39,8 +39,8 @@ "access": "public" }, "dependencies": { - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/deno/package.json b/packages/deno/package.json index 2e45b6cab9a7..0462939ae06f 100644 --- a/packages/deno/package.json +++ b/packages/deno/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/deno", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Deno", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/deno", @@ -24,9 +24,9 @@ "/build" ], "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "devDependencies": { "@rollup/plugin-typescript": "^11.1.5", diff --git a/packages/ember/package.json b/packages/ember/package.json index 86cee9a07704..2ff37964532e 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/ember", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Ember.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/ember", @@ -33,10 +33,10 @@ "dependencies": { "@babel/core": "^7.24.4", "@embroider/macros": "^1.16.0", - "@sentry/browser": "8.27.0", - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/browser": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "ember-auto-import": "^2.7.2", "ember-cli-babel": "^8.2.0", "ember-cli-htmlbars": "^6.1.1", diff --git a/packages/eslint-config-sdk/package.json b/packages/eslint-config-sdk/package.json index 9150ae217cb9..3da6854a5c3c 100644 --- a/packages/eslint-config-sdk/package.json +++ b/packages/eslint-config-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-config-sdk", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK eslint config", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-config-sdk", @@ -22,8 +22,8 @@ "access": "public" }, "dependencies": { - "@sentry-internal/eslint-plugin-sdk": "8.27.0", - "@sentry-internal/typescript": "8.27.0", + "@sentry-internal/eslint-plugin-sdk": "8.28.0", + "@sentry-internal/typescript": "8.28.0", "@typescript-eslint/eslint-plugin": "^5.48.0", "@typescript-eslint/parser": "^5.48.0", "eslint-config-prettier": "^6.11.0", diff --git a/packages/eslint-plugin-sdk/package.json b/packages/eslint-plugin-sdk/package.json index 9d947d2c3f03..577bce3380a9 100644 --- a/packages/eslint-plugin-sdk/package.json +++ b/packages/eslint-plugin-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/eslint-plugin-sdk", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK eslint plugin", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/eslint-plugin-sdk", diff --git a/packages/feedback/package.json b/packages/feedback/package.json index d69f42e7d896..b635b2dd46c8 100644 --- a/packages/feedback/package.json +++ b/packages/feedback/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/feedback", - "version": "8.27.0", + "version": "8.28.0", "description": "Sentry SDK integration for user feedback", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/feedback", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "devDependencies": { "preact": "^10.19.4" diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 8cfaccb63c48..013d02cd8d84 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/gatsby", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Gatsby.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/gatsby", @@ -45,10 +45,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/react": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/core": "8.28.0", + "@sentry/react": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "@sentry/webpack-plugin": "2.22.3" }, "peerDependencies": { diff --git a/packages/google-cloud-serverless/package.json b/packages/google-cloud-serverless/package.json index 2d5e364984cd..72c5a0b94d9a 100644 --- a/packages/google-cloud-serverless/package.json +++ b/packages/google-cloud-serverless/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/google-cloud-serverless", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Google Cloud Functions", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/google-cloud", @@ -48,10 +48,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/node": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/core": "8.28.0", + "@sentry/node": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "@types/express": "^4.17.14" }, "devDependencies": { diff --git a/packages/integration-shims/package.json b/packages/integration-shims/package.json index ff8dfc0f155b..70c7737fcdca 100644 --- a/packages/integration-shims/package.json +++ b/packages/integration-shims/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/integration-shims", - "version": "8.27.0", + "version": "8.28.0", "description": "Shims for integrations in Sentry SDK.", "main": "build/cjs/index.js", "module": "build/esm/index.js", @@ -55,9 +55,9 @@ "url": "https://github.com/getsentry/sentry-javascript/issues" }, "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { "node": ">=14.18" diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index 9d084af2a92f..65fa623595e7 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nestjs", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for NestJS", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nestjs", @@ -44,10 +44,10 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/node": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/core": "8.28.0", + "@sentry/node": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "devDependencies": { "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 2d9e110b078f..6965ddcf5167 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nextjs", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Next.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nextjs", @@ -71,13 +71,13 @@ "@opentelemetry/instrumentation-http": "0.52.1", "@opentelemetry/semantic-conventions": "^1.25.1", "@rollup/plugin-commonjs": "26.0.1", - "@sentry/core": "8.27.0", - "@sentry/node": "8.27.0", - "@sentry/opentelemetry": "8.27.0", - "@sentry/react": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", - "@sentry/vercel-edge": "8.27.0", + "@sentry/core": "8.28.0", + "@sentry/node": "8.28.0", + "@sentry/opentelemetry": "8.28.0", + "@sentry/react": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", + "@sentry/vercel-edge": "8.28.0", "@sentry/webpack-plugin": "2.22.3", "chalk": "3.0.0", "resolve": "1.22.8", diff --git a/packages/node/package.json b/packages/node/package.json index e6a1b0a7feec..f17c37541f63 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/node", - "version": "8.27.0", + "version": "8.28.0", "description": "Sentry Node SDK using OpenTelemetry for performance instrumentation", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/node", @@ -89,10 +89,10 @@ "@opentelemetry/sdk-trace-base": "^1.25.1", "@opentelemetry/semantic-conventions": "^1.25.1", "@prisma/instrumentation": "5.18.0", - "@sentry/core": "8.27.0", - "@sentry/opentelemetry": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/core": "8.28.0", + "@sentry/opentelemetry": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "import-in-the-middle": "^1.11.0" }, "devDependencies": { diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 16c9d46754d5..2aaa9f06dd78 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/nuxt", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Nuxt (EXPERIMENTAL)", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nuxt", @@ -43,14 +43,14 @@ }, "dependencies": { "@nuxt/kit": "^3.12.2", - "@sentry/browser": "8.27.0", - "@sentry/core": "8.27.0", - "@sentry/node": "8.27.0", - "@sentry/opentelemetry": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/browser": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/node": "8.28.0", + "@sentry/opentelemetry": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "@sentry/vite-plugin": "2.22.3", - "@sentry/vue": "8.27.0" + "@sentry/vue": "8.28.0" }, "devDependencies": { "@nuxt/module-builder": "0.8.1", diff --git a/packages/opentelemetry/package.json b/packages/opentelemetry/package.json index 34c498aa74c5..fadbcadc6f5e 100644 --- a/packages/opentelemetry/package.json +++ b/packages/opentelemetry/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/opentelemetry", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry utilities for OpenTelemetry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/opentelemetry", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", diff --git a/packages/profiling-node/package.json b/packages/profiling-node/package.json index 42795df3d0bc..504706ab6418 100644 --- a/packages/profiling-node/package.json +++ b/packages/profiling-node/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/profiling-node", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Node.js Profiling", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/profiling-node", @@ -75,10 +75,10 @@ "test": "cross-env SENTRY_PROFILER_BINARY_DIR=lib jest --config jest.config.js" }, "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/node": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/core": "8.28.0", + "@sentry/node": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "detect-libc": "^2.0.2", "node-abi": "^3.61.0" }, diff --git a/packages/react/package.json b/packages/react/package.json index f5e11fe94f99..0afed2e367ad 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/react", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for React.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/react", @@ -39,10 +39,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.27.0", - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/browser": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "hoist-non-react-statics": "^3.3.2" }, "peerDependencies": { diff --git a/packages/remix/package.json b/packages/remix/package.json index a7ba4b4dae0b..286926c79f1d 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/remix", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Remix", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/remix", @@ -55,12 +55,12 @@ "@opentelemetry/instrumentation-http": "0.52.1", "@remix-run/router": "1.x", "@sentry/cli": "^2.33.0", - "@sentry/core": "8.27.0", - "@sentry/node": "8.27.0", - "@sentry/opentelemetry": "8.27.0", - "@sentry/react": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/core": "8.28.0", + "@sentry/node": "8.28.0", + "@sentry/opentelemetry": "8.28.0", + "@sentry/react": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "glob": "^10.3.4", "opentelemetry-instrumentation-remix": "0.7.1", "yargs": "^17.6.0" diff --git a/packages/replay-canvas/package.json b/packages/replay-canvas/package.json index 1c3a8c202286..e28e297b2b72 100644 --- a/packages/replay-canvas/package.json +++ b/packages/replay-canvas/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/replay-canvas", - "version": "8.27.0", + "version": "8.28.0", "description": "Replay canvas integration", "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", @@ -68,10 +68,10 @@ "@sentry-internal/rrweb": "2.26.0" }, "dependencies": { - "@sentry-internal/replay": "8.27.0", - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry-internal/replay": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { "node": ">=14.18" diff --git a/packages/replay-internal/package.json b/packages/replay-internal/package.json index d7b602af7dc1..fe9221915e62 100644 --- a/packages/replay-internal/package.json +++ b/packages/replay-internal/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/replay", - "version": "8.27.0", + "version": "8.28.0", "description": "User replays for Sentry", "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", @@ -68,7 +68,7 @@ "homepage": "https://docs.sentry.io/platforms/javascript/session-replay/", "devDependencies": { "@babel/core": "^7.17.5", - "@sentry-internal/replay-worker": "8.27.0", + "@sentry-internal/replay-worker": "8.28.0", "@sentry-internal/rrweb": "2.26.0", "@sentry-internal/rrweb-snapshot": "2.26.0", "fflate": "^0.8.1", @@ -76,10 +76,10 @@ "jsdom-worker": "^0.2.1" }, "dependencies": { - "@sentry-internal/browser-utils": "8.27.0", - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry-internal/browser-utils": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { "node": ">=14.18" diff --git a/packages/replay-worker/package.json b/packages/replay-worker/package.json index d62c06034c49..126c3b48cb20 100644 --- a/packages/replay-worker/package.json +++ b/packages/replay-worker/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/replay-worker", - "version": "8.27.0", + "version": "8.28.0", "description": "Worker for @sentry-internal/replay", "main": "build/esm/index.js", "module": "build/esm/index.js", diff --git a/packages/solid/package.json b/packages/solid/package.json index be3e51a131c4..7ae9f47239cd 100644 --- a/packages/solid/package.json +++ b/packages/solid/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/solid", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Solid", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/solid", @@ -44,10 +44,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.27.0", - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/browser": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "peerDependencies": { "@solidjs/router": "^0.13.4", diff --git a/packages/solidstart/package.json b/packages/solidstart/package.json index efb530697ff6..80de3c6b5b36 100644 --- a/packages/solidstart/package.json +++ b/packages/solidstart/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/solidstart", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Solid Start", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/solidstart", @@ -67,12 +67,12 @@ }, "dependencies": { "@opentelemetry/instrumentation": "^0.52.1", - "@sentry/core": "8.27.0", - "@sentry/node": "8.27.0", - "@sentry/opentelemetry": "8.27.0", - "@sentry/solid": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/core": "8.28.0", + "@sentry/node": "8.28.0", + "@sentry/opentelemetry": "8.28.0", + "@sentry/solid": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "@sentry/vite-plugin": "2.22.3" }, "devDependencies": { diff --git a/packages/svelte/package.json b/packages/svelte/package.json index c03f0ba2ec48..ea9ee95b49d7 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/svelte", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Svelte", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/svelte", @@ -39,10 +39,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.27.0", - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/browser": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "magic-string": "^0.30.0" }, "peerDependencies": { diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json index f5af15b667d3..0a47d189fd8c 100644 --- a/packages/sveltekit/package.json +++ b/packages/sveltekit/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/sveltekit", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for SvelteKit", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/sveltekit", @@ -40,12 +40,12 @@ } }, "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/node": "8.27.0", - "@sentry/opentelemetry": "8.27.0", - "@sentry/svelte": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0", + "@sentry/core": "8.28.0", + "@sentry/node": "8.28.0", + "@sentry/opentelemetry": "8.28.0", + "@sentry/svelte": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "@sentry/vite-plugin": "2.22.3", "magic-string": "0.30.7", "magicast": "0.2.8", diff --git a/packages/types/package.json b/packages/types/package.json index 5b92d1766c7d..5d9a011c0677 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/types", - "version": "8.27.0", + "version": "8.28.0", "description": "Types for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/types", diff --git a/packages/typescript/package.json b/packages/typescript/package.json index 165b6db47a79..1aeea0611add 100644 --- a/packages/typescript/package.json +++ b/packages/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/typescript", - "version": "8.27.0", + "version": "8.28.0", "description": "Typescript configuration used at Sentry", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/typescript", diff --git a/packages/utils/package.json b/packages/utils/package.json index ffd3cae5b3c5..0f11c016dde3 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/utils", - "version": "8.27.0", + "version": "8.28.0", "description": "Utilities for all Sentry JavaScript SDKs", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/utils", @@ -39,7 +39,7 @@ "access": "public" }, "dependencies": { - "@sentry/types": "8.27.0" + "@sentry/types": "8.28.0" }, "devDependencies": { "@types/array.prototype.flat": "^1.2.1", diff --git a/packages/utils/src/version.ts b/packages/utils/src/version.ts index 424a201c405f..a42658a94943 100644 --- a/packages/utils/src/version.ts +++ b/packages/utils/src/version.ts @@ -1 +1 @@ -export const SDK_VERSION = '8.27.0'; +export const SDK_VERSION = '8.28.0'; diff --git a/packages/vercel-edge/package.json b/packages/vercel-edge/package.json index 09f9860418ae..3ff7c934eba1 100644 --- a/packages/vercel-edge/package.json +++ b/packages/vercel-edge/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vercel-edge", - "version": "8.27.0", + "version": "8.28.0", "description": "Offical Sentry SDK for the Vercel Edge Runtime", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vercel-edge", @@ -39,9 +39,9 @@ "access": "public" }, "dependencies": { - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "devDependencies": { "@edge-runtime/types": "3.0.1" diff --git a/packages/vue/package.json b/packages/vue/package.json index f1604d24e948..6e6022c58236 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/vue", - "version": "8.27.0", + "version": "8.28.0", "description": "Official Sentry SDK for Vue.js", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vue", @@ -39,10 +39,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.27.0", - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/browser": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "peerDependencies": { "vue": "2.x || 3.x" diff --git a/packages/wasm/package.json b/packages/wasm/package.json index 5626e5096152..961c78f9e9b2 100644 --- a/packages/wasm/package.json +++ b/packages/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@sentry/wasm", - "version": "8.27.0", + "version": "8.28.0", "description": "Support for WASM.", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/wasm", @@ -39,10 +39,10 @@ "access": "public" }, "dependencies": { - "@sentry/browser": "8.27.0", - "@sentry/core": "8.27.0", - "@sentry/types": "8.27.0", - "@sentry/utils": "8.27.0" + "@sentry/browser": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "scripts": { "build": "run-p build:transpile build:bundle build:types", From 47743990e3e2249e5c48ec3639bb849deda5db50 Mon Sep 17 00:00:00 2001 From: Kaung Zin Hein <83657429+Zen-cronic@users.noreply.github.com> Date: Tue, 3 Sep 2024 08:04:47 -0400 Subject: [PATCH 54/64] feat(node): Add `generic-pool` integration (#13465) Resolves #13308 Implement genericPool OTL instrumentation in `packages/node` - [x] If you've added code that should be tested, please add tests. - [x] Ensure your code lints and the test suite passes (`yarn lint`) & (`yarn test`). --------- Signed-off-by: Kaung Zin Hein --- .../node-integration-tests/package.json | 1 + .../suites/tracing/genericPool/scenario.js | 71 +++++++++++++++++++ .../suites/tracing/genericPool/test.ts | 34 +++++++++ packages/astro/src/index.server.ts | 1 + packages/aws-serverless/src/index.ts | 1 + packages/bun/src/index.ts | 1 + packages/google-cloud-serverless/src/index.ts | 1 + packages/node/package.json | 1 + packages/node/src/index.ts | 1 + .../src/integrations/tracing/genericPool.ts | 40 +++++++++++ .../node/src/integrations/tracing/index.ts | 3 + packages/remix/src/index.server.ts | 1 + packages/solidstart/src/server/index.ts | 1 + packages/sveltekit/src/server/index.ts | 1 + yarn.lock | 9 ++- 15 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 dev-packages/node-integration-tests/suites/tracing/genericPool/scenario.js create mode 100644 dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts create mode 100644 packages/node/src/integrations/tracing/genericPool.ts diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index 9243a1512884..0ac7fdbf0aa2 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -44,6 +44,7 @@ "cors": "^2.8.5", "cron": "^3.1.6", "express": "^4.17.3", + "generic-pool": "^3.9.0", "graphql": "^16.3.0", "http-terminator": "^3.2.0", "ioredis": "^5.4.1", diff --git a/dev-packages/node-integration-tests/suites/tracing/genericPool/scenario.js b/dev-packages/node-integration-tests/suites/tracing/genericPool/scenario.js new file mode 100644 index 000000000000..74d5f73693f5 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/genericPool/scenario.js @@ -0,0 +1,71 @@ +const { loggingTransport } = require('@sentry-internal/node-integration-tests'); +const Sentry = require('@sentry/node'); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + transport: loggingTransport, +}); + +// Stop the process from exiting before the transaction is sent +setInterval(() => {}, 1000); + +const mysql = require('mysql'); +const genericPool = require('generic-pool'); + +const factory = { + create: function () { + return mysql.createConnection({ + user: 'root', + password: 'docker', + }); + }, + destroy: function (client) { + client.end(err => { + if (err) { + // eslint-disable-next-line no-console + console.error('Error while disconnecting MySQL:', err); + } + }); + }, +}; + +const opts = { + max: 10, + min: 2, +}; + +const myPool = genericPool.createPool(factory, opts); + +async function run() { + await Sentry.startSpan( + { + op: 'transaction', + name: 'Test Transaction', + }, + async () => { + try { + const client1 = await myPool.acquire(); + const client2 = await myPool.acquire(); + + client1.query('SELECT NOW()', function () { + myPool.release(client1); + }); + + client2.query('SELECT 1 + 1 AS solution', function () { + myPool.release(client2); + }); + } catch (err) { + // eslint-disable-next-line no-console + console.error('Error while pooling MySQL:', err); + } finally { + await myPool.drain(); + await myPool.clear(); + } + }, + ); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts b/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts new file mode 100644 index 000000000000..a61782ae0fe5 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts @@ -0,0 +1,34 @@ +import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; + +describe('genericPool auto instrumentation', () => { + afterAll(() => { + cleanupChildProcesses(); + }); + + test('should auto-instrument `genericPool` package when calling pool.require()', done => { + const EXPECTED_TRANSACTION = { + transaction: 'Test Transaction', + spans: expect.arrayContaining([ + expect.objectContaining({ + description: expect.stringMatching(/^generic-pool\.ac?quire/), + origin: 'auto.db.otel.generic-pool', + data: { + 'sentry.origin': 'auto.db.otel.generic-pool', + }, + status: 'ok', + }), + + expect.objectContaining({ + description: expect.stringMatching(/^generic-pool\.ac?quire/), + origin: 'auto.db.otel.generic-pool', + data: { + 'sentry.origin': 'auto.db.otel.generic-pool', + }, + status: 'ok', + }), + ]), + }; + + createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done); + }); +}); diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index 0895eb86e364..747870da3014 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -43,6 +43,7 @@ export { fsIntegration, functionToStringIntegration, generateInstrumentOnce, + genericPoolIntegration, getActiveSpan, getAutoPerformanceIntegrations, getClient, diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 27f48c2da0db..f648dba045ec 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -87,6 +87,7 @@ export { setupConnectErrorHandler, fastifyIntegration, fsIntegration, + genericPoolIntegration, graphqlIntegration, mongoIntegration, mongooseIntegration, diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index d4ca06f85584..1d8b02c33568 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -108,6 +108,7 @@ export { setupKoaErrorHandler, connectIntegration, setupConnectErrorHandler, + genericPoolIntegration, graphqlIntegration, mongoIntegration, mongooseIntegration, diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index f58305c64f6c..463a0c5c1246 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -87,6 +87,7 @@ export { connectIntegration, setupConnectErrorHandler, fastifyIntegration, + genericPoolIntegration, graphqlIntegration, mongoIntegration, mongooseIntegration, diff --git a/packages/node/package.json b/packages/node/package.json index f17c37541f63..6a64f646f64c 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -73,6 +73,7 @@ "@opentelemetry/instrumentation-express": "0.41.1", "@opentelemetry/instrumentation-fastify": "0.38.0", "@opentelemetry/instrumentation-fs": "0.14.0", + "@opentelemetry/instrumentation-generic-pool": "0.38.0", "@opentelemetry/instrumentation-graphql": "0.42.0", "@opentelemetry/instrumentation-hapi": "0.40.0", "@opentelemetry/instrumentation-http": "0.52.1", diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 5f4adf315fd5..6ce3c325e3ff 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -26,6 +26,7 @@ export { hapiIntegration, setupHapiErrorHandler } from './integrations/tracing/h export { koaIntegration, setupKoaErrorHandler } from './integrations/tracing/koa'; export { connectIntegration, setupConnectErrorHandler } from './integrations/tracing/connect'; export { spotlightIntegration } from './integrations/spotlight'; +export { genericPoolIntegration } from './integrations/tracing/genericPool'; export { SentryContextManager } from './otel/contextManager'; export { generateInstrumentOnce } from './otel/instrument'; diff --git a/packages/node/src/integrations/tracing/genericPool.ts b/packages/node/src/integrations/tracing/genericPool.ts new file mode 100644 index 000000000000..55b8b6095f64 --- /dev/null +++ b/packages/node/src/integrations/tracing/genericPool.ts @@ -0,0 +1,40 @@ +import { GenericPoolInstrumentation } from '@opentelemetry/instrumentation-generic-pool'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration, spanToJSON } from '@sentry/core'; +import type { IntegrationFn } from '@sentry/types'; +import { generateInstrumentOnce } from '../../otel/instrument'; + +const INTEGRATION_NAME = 'GenericPool'; + +export const instrumentGenericPool = generateInstrumentOnce(INTEGRATION_NAME, () => new GenericPoolInstrumentation({})); + +const _genericPoolIntegration = (() => { + return { + name: INTEGRATION_NAME, + setupOnce() { + instrumentGenericPool(); + }, + + setup(client) { + client.on('spanStart', span => { + const spanJSON = spanToJSON(span); + + const spanDescription = spanJSON.description; + + // typo in emitted span for version <= 0.38.0 of @opentelemetry/instrumentation-generic-pool + const isGenericPoolSpan = + spanDescription === 'generic-pool.aquire' || spanDescription === 'generic-pool.acquire'; + + if (isGenericPoolSpan) { + span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.db.otel.generic-pool'); + } + }); + }, + }; +}) satisfies IntegrationFn; + +/** + * GenericPool integration + * + * Capture tracing data for GenericPool. + */ +export const genericPoolIntegration = defineIntegration(_genericPoolIntegration); diff --git a/packages/node/src/integrations/tracing/index.ts b/packages/node/src/integrations/tracing/index.ts index 886c11683674..46a9f79e4caa 100644 --- a/packages/node/src/integrations/tracing/index.ts +++ b/packages/node/src/integrations/tracing/index.ts @@ -4,6 +4,7 @@ import { instrumentHttp } from '../http'; import { connectIntegration, instrumentConnect } from './connect'; import { expressIntegration, instrumentExpress } from './express'; import { fastifyIntegration, instrumentFastify } from './fastify'; +import { genericPoolIntegration, instrumentGenericPool } from './genericPool'; import { graphqlIntegration, instrumentGraphql } from './graphql'; import { hapiIntegration, instrumentHapi } from './hapi'; import { instrumentKoa, koaIntegration } from './koa'; @@ -37,6 +38,7 @@ export function getAutoPerformanceIntegrations(): Integration[] { hapiIntegration(), koaIntegration(), connectIntegration(), + genericPoolIntegration(), ]; } @@ -61,5 +63,6 @@ export function getOpenTelemetryInstrumentationToPreload(): (((options?: any) => instrumentHapi, instrumentGraphql, instrumentRedis, + instrumentGenericPool, ]; } diff --git a/packages/remix/src/index.server.ts b/packages/remix/src/index.server.ts index 7ab6efb15827..457dcb9f8685 100644 --- a/packages/remix/src/index.server.ts +++ b/packages/remix/src/index.server.ts @@ -47,6 +47,7 @@ export { flush, functionToStringIntegration, generateInstrumentOnce, + genericPoolIntegration, getActiveSpan, getAutoPerformanceIntegrations, getClient, diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index c3499a82459a..995f58d057e3 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -38,6 +38,7 @@ export { flush, functionToStringIntegration, generateInstrumentOnce, + genericPoolIntegration, getActiveSpan, getAutoPerformanceIntegrations, getClient, diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index 32dd6627d7a6..d57ec35bd7cc 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -37,6 +37,7 @@ export { fastifyIntegration, flush, functionToStringIntegration, + genericPoolIntegration, generateInstrumentOnce, getActiveSpan, getAutoPerformanceIntegrations, diff --git a/yarn.lock b/yarn.lock index 6f2432d7054a..1acd7e11fbd9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7113,6 +7113,13 @@ "@opentelemetry/core" "^1.8.0" "@opentelemetry/instrumentation" "^0.52.0" +"@opentelemetry/instrumentation-generic-pool@0.38.0": + version "0.38.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.38.0.tgz#9ea4d82da23541cda613d553bd405b2cbc0da184" + integrity sha512-0/ULi6pIco1fEnDPmmAul8ZoudFL7St0hjgBbWZlZPBCSyslDll1J7DFeEbjiRSSyUd+0tu73ae0DOKVKNd7VA== + dependencies: + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/instrumentation-graphql@0.42.0": version "0.42.0" resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.42.0.tgz#588a18c39e3b3f655bc09243566172ab0b638d35" @@ -19084,7 +19091,7 @@ generate-function@^2.3.1: dependencies: is-property "^1.0.2" -generic-pool@3.9.0: +generic-pool@3.9.0, generic-pool@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== From b7d154440fc8d40f1154c67129dc80cc8723d184 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:34:55 +0200 Subject: [PATCH 55/64] feat(vue): Only start UI spans if parent is available (#13568) UI spans make sense if they are a child of e.g. a pageload root span. This change ensures UI spans are not created as root spans. ref https://github.com/getsentry/sentry-javascript/issues/13546 --- packages/vue/src/tracing.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/vue/src/tracing.ts b/packages/vue/src/tracing.ts index 70f662559adf..135b7fa8c9cc 100644 --- a/packages/vue/src/tracing.ts +++ b/packages/vue/src/tracing.ts @@ -114,6 +114,8 @@ export const createTracingMixins = (options: TracingOptions): Mixins => { attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.vue', }, + // UI spans should only be created if there is an active root span (transaction) + onlyIfParent: true, }); } } else { From 3180113e7a62fb283298fa47364d2d6bef6d37b0 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 3 Sep 2024 16:50:28 +0200 Subject: [PATCH 56/64] ci: Show what size limit jobs exceeded the limit (#13532) Noticed that it was a bit annoying, when we exceeded the size limit, to find out why/what has failed. Now, it should show in the GH comment which specific check has failed, and what the current limit is: ![image](https://github.com/user-attachments/assets/13495922-ac91-42bf-a398-18bb7b0e9e72) While I was at this, I also streamlined the code a bit, extracted it a bit into utils to de-clutter stuff, and fixed/improved some debug logging issues (e.g. the log group was not correctly closed before, ...) --- .../size-limit-gh-action/.eslintrc.cjs | 2 +- dev-packages/size-limit-gh-action/index.mjs | 319 +++--------------- .../utils/SizeLimitFormatter.mjs | 137 ++++++++ .../getArtifactsForBranchAndWorkflow.mjs | 128 +++++++ 4 files changed, 312 insertions(+), 274 deletions(-) create mode 100644 dev-packages/size-limit-gh-action/utils/SizeLimitFormatter.mjs create mode 100644 dev-packages/size-limit-gh-action/utils/getArtifactsForBranchAndWorkflow.mjs diff --git a/dev-packages/size-limit-gh-action/.eslintrc.cjs b/dev-packages/size-limit-gh-action/.eslintrc.cjs index 8c67e0037908..4a92730d3b0b 100644 --- a/dev-packages/size-limit-gh-action/.eslintrc.cjs +++ b/dev-packages/size-limit-gh-action/.eslintrc.cjs @@ -7,7 +7,7 @@ module.exports = { overrides: [ { - files: ['*.mjs'], + files: ['**/*.mjs'], extends: ['@sentry-internal/sdk/src/base'], }, ], diff --git a/dev-packages/size-limit-gh-action/index.mjs b/dev-packages/size-limit-gh-action/index.mjs index 3dbb8aa22127..680d12237bf5 100644 --- a/dev-packages/size-limit-gh-action/index.mjs +++ b/dev-packages/size-limit-gh-action/index.mjs @@ -1,4 +1,3 @@ -/* eslint-disable max-lines */ import { promises as fs } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -9,13 +8,22 @@ import { exec } from '@actions/exec'; import { context, getOctokit } from '@actions/github'; import * as glob from '@actions/glob'; import * as io from '@actions/io'; -import bytes from 'bytes'; import { markdownTable } from 'markdown-table'; +import { SizeLimitFormatter } from './utils/SizeLimitFormatter.mjs'; +import { getArtifactsForBranchAndWorkflow } from './utils/getArtifactsForBranchAndWorkflow.mjs'; + const SIZE_LIMIT_HEADING = '## size-limit report 📦 '; const ARTIFACT_NAME = 'size-limit-action'; const RESULTS_FILE = 'size-limit-results.json'; +function getResultsFilePath() { + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + return path.resolve(__dirname, RESULTS_FILE); +} + +const { getInput, setFailed } = core; + async function fetchPreviousComment(octokit, repo, pr) { const { data: commentList } = await octokit.rest.issues.listComments({ ...repo, @@ -26,114 +34,6 @@ async function fetchPreviousComment(octokit, repo, pr) { return !sizeLimitComment ? null : sizeLimitComment; } -class SizeLimit { - formatBytes(size) { - return bytes.format(size, { unitSeparator: ' ' }); - } - - formatPercentageChange(base = 0, current = 0) { - if (base === 0) { - return 'added'; - } - - if (current === 0) { - return 'removed'; - } - - const value = ((current - base) / base) * 100; - const formatted = (Math.sign(value) * Math.ceil(Math.abs(value) * 100)) / 100; - - if (value > 0) { - return `+${formatted}%`; - } - - if (value === 0) { - return '-'; - } - - return `${formatted}%`; - } - - formatChange(base = 0, current = 0) { - if (base === 0) { - return 'added'; - } - - if (current === 0) { - return 'removed'; - } - - const value = current - base; - const formatted = this.formatBytes(value); - - if (value > 0) { - return `+${formatted} 🔺`; - } - - if (value === 0) { - return '-'; - } - - return `${formatted} 🔽`; - } - - formatLine(value, change) { - return `${value} (${change})`; - } - - formatSizeResult(name, base, current) { - return [ - name, - this.formatBytes(current.size), - this.formatPercentageChange(base.size, current.size), - this.formatChange(base.size, current.size), - ]; - } - - parseResults(output) { - const results = JSON.parse(output); - - return results.reduce((current, result) => { - return { - // biome-ignore lint/performance/noAccumulatingSpread: - ...current, - [result.name]: { - name: result.name, - size: +result.size, - }, - }; - }, {}); - } - - hasSizeChanges(base, current, threshold = 0) { - const names = [...new Set([...(base ? Object.keys(base) : []), ...Object.keys(current)])]; - - return !!names.find(name => { - const baseResult = base?.[name] || EmptyResult; - const currentResult = current[name] || EmptyResult; - - if (baseResult.size === 0 && currentResult.size === 0) { - return true; - } - - return Math.abs((currentResult.size - baseResult.size) / baseResult.size) * 100 > threshold; - }); - } - - formatResults(base, current) { - const names = [...new Set([...(base ? Object.keys(base) : []), ...Object.keys(current)])]; - const header = SIZE_RESULTS_HEADER; - const fields = names.map(name => { - const baseResult = base?.[name] || EmptyResult; - const currentResult = current[name] || EmptyResult; - - return this.formatSizeResult(name, baseResult, currentResult); - }); - - return [header, ...fields]; - } -} - async function execSizeLimit() { let output = ''; @@ -151,15 +51,8 @@ async function execSizeLimit() { return { status, output }; } -const SIZE_RESULTS_HEADER = ['Path', 'Size', '% Change', 'Change']; - -const EmptyResult = { - name: '-', - size: 0, -}; - async function run() { - const { getInput, setFailed } = core; + const __dirname = path.dirname(fileURLToPath(import.meta.url)); try { const { payload, repo } = context; @@ -174,36 +67,12 @@ async function run() { } const octokit = getOctokit(githubToken); - const limit = new SizeLimit(); - const artifactClient = artifact.create(); - const __dirname = path.dirname(fileURLToPath(import.meta.url)); - const resultsFilePath = path.resolve(__dirname, RESULTS_FILE); + const limit = new SizeLimitFormatter(); + const resultsFilePath = getResultsFilePath(); // If we have no comparison branch, we just run size limit & store the result as artifact if (!comparisonBranch) { - let base; - const { output: baseOutput } = await execSizeLimit(); - - try { - base = limit.parseResults(baseOutput); - } catch (error) { - core.error('Error parsing size-limit output. The output should be a json.'); - throw error; - } - - try { - await fs.writeFile(resultsFilePath, JSON.stringify(base), 'utf8'); - } catch (err) { - core.error(err); - } - const globber = await glob.create(resultsFilePath, { - followSymbolicLinks: false, - }); - const files = await globber.glob(); - - await artifactClient.uploadArtifact(ARTIFACT_NAME, files, __dirname); - - return; + return runSizeLimitOnComparisonBranch(); } // Else, we run size limit for the current branch, AND fetch it for the comparison branch @@ -213,12 +82,15 @@ async function run() { let baseWorkflowRun; try { + const workflowName = `${process.env.GITHUB_WORKFLOW || ''}`; + core.startGroup(`getArtifactsForBranchAndWorkflow - workflow:"${workflowName}", branch:"${comparisonBranch}"`); const artifacts = await getArtifactsForBranchAndWorkflow(octokit, { ...repo, artifactName: ARTIFACT_NAME, branch: comparisonBranch, - workflowName: `${process.env.GITHUB_WORKFLOW || ''}`, + workflowName, }); + core.endGroup(); if (!artifacts) { throw new Error('No artifacts found'); @@ -255,9 +127,12 @@ async function run() { const thresholdNumber = Number(threshold); - // @ts-ignore const sizeLimitComment = await fetchPreviousComment(octokit, repo, pr); + if (sizeLimitComment) { + core.debug('Found existing size limit comment, udpating it instead of creating a new one...'); + } + const shouldComment = isNaN(thresholdNumber) || limit.hasSizeChanges(base, current, thresholdNumber) || sizeLimitComment; @@ -269,8 +144,12 @@ async function run() { '⚠️ **Warning:** Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.', ); } - - bodyParts.push(markdownTable(limit.formatResults(base, current))); + try { + bodyParts.push(markdownTable(limit.formatResults(base, current))); + } catch (error) { + core.error('Error generating markdown table'); + core.error(error); + } if (baseWorkflowRun) { bodyParts.push(''); @@ -298,6 +177,8 @@ async function run() { "Error updating comment. This can happen for PR's originating from a fork without write permissions.", ); } + } else { + core.debug('Skipping comment because there are no changes.'); } if (status > 0) { @@ -309,136 +190,28 @@ async function run() { } } -// max pages of workflows to pagination through -const DEFAULT_MAX_PAGES = 50; -// max results per page -const DEFAULT_PAGE_LIMIT = 10; - -/** - * Fetch artifacts from a workflow run from a branch - * - * This is a bit hacky since GitHub Actions currently does not directly - * support downloading artifacts from other workflows - */ -/** - * Fetch artifacts from a workflow run from a branch - * - * This is a bit hacky since GitHub Actions currently does not directly - * support downloading artifacts from other workflows - */ -async function getArtifactsForBranchAndWorkflow(octokit, { owner, repo, workflowName, branch, artifactName }) { - core.startGroup(`getArtifactsForBranchAndWorkflow - workflow:"${workflowName}", branch:"${branch}"`); - - let repositoryWorkflow = null; +async function runSizeLimitOnComparisonBranch() { + const resultsFilePath = getResultsFilePath(); - // For debugging - const allWorkflows = []; - - // - // Find workflow id from `workflowName` - // - for await (const response of octokit.paginate.iterator(octokit.rest.actions.listRepoWorkflows, { - owner, - repo, - })) { - const targetWorkflow = response.data.find(({ name }) => name === workflowName); + const limit = new SizeLimitFormatter(); + const artifactClient = artifact.create(); - allWorkflows.push(...response.data.map(({ name }) => name)); + const { output: baseOutput } = await execSizeLimit(); - // If not found in responses, continue to search on next page - if (!targetWorkflow) { - continue; - } - - repositoryWorkflow = targetWorkflow; - break; - } - - if (!repositoryWorkflow) { - core.info( - `Unable to find workflow with name "${workflowName}" in the repository. Found workflows: ${allWorkflows.join( - ', ', - )}`, - ); - core.endGroup(); - return null; + try { + const base = limit.parseResults(baseOutput); + await fs.writeFile(resultsFilePath, JSON.stringify(base), 'utf8'); + } catch (error) { + core.error('Error parsing size-limit output. The output should be a json.'); + throw error; } - const workflow_id = repositoryWorkflow.id; - - let currentPage = 0; - let latestWorkflowRun = null; - - for await (const response of octokit.paginate.iterator(octokit.rest.actions.listWorkflowRuns, { - owner, - repo, - workflow_id, - branch, - per_page: DEFAULT_PAGE_LIMIT, - event: 'push', - })) { - if (!response.data.length) { - core.warning(`Workflow ${workflow_id} not found in branch ${branch}`); - core.endGroup(); - return null; - } - - // Do not allow downloading artifacts from a fork. - const filtered = response.data.filter(workflowRun => workflowRun.head_repository.full_name === `${owner}/${repo}`); - - // Sort to ensure the latest workflow run is the first - filtered.sort((a, b) => { - return new Date(b.created_at).getTime() - new Date(a.created_at).getTime(); - }); - - // Store the first workflow run, to determine if this is the latest one... - if (!latestWorkflowRun) { - latestWorkflowRun = filtered[0]; - } - - // Search through workflow artifacts until we find a workflow run w/ artifact name that we are looking for - for (const workflowRun of filtered) { - core.info(`Checking artifacts for workflow run: ${workflowRun.html_url}`); - - const { - data: { artifacts }, - } = await octokit.rest.actions.listWorkflowRunArtifacts({ - owner, - repo, - run_id: workflowRun.id, - }); - - if (!artifacts) { - core.warning( - `Unable to fetch artifacts for branch: ${branch}, workflow: ${workflow_id}, workflowRunId: ${workflowRun.id}`, - ); - } else { - const foundArtifact = artifacts.find(({ name }) => name === artifactName); - if (foundArtifact) { - core.info(`Found suitable artifact: ${foundArtifact.url}`); - return { - artifact: foundArtifact, - workflowRun, - isLatest: latestWorkflowRun.id === workflowRun.id, - }; - } else { - core.info(`No artifact found for ${artifactName}, trying next workflow run...`); - } - } - } - - if (currentPage > DEFAULT_MAX_PAGES) { - core.warning(`Workflow ${workflow_id} not found in branch: ${branch}`); - core.endGroup(); - return null; - } - - currentPage++; - } + const globber = await glob.create(resultsFilePath, { + followSymbolicLinks: false, + }); + const files = await globber.glob(); - core.warning(`Artifact not found: ${artifactName}`); - core.endGroup(); - return null; + await artifactClient.uploadArtifact(ARTIFACT_NAME, files, __dirname); } run(); diff --git a/dev-packages/size-limit-gh-action/utils/SizeLimitFormatter.mjs b/dev-packages/size-limit-gh-action/utils/SizeLimitFormatter.mjs new file mode 100644 index 000000000000..034281b38224 --- /dev/null +++ b/dev-packages/size-limit-gh-action/utils/SizeLimitFormatter.mjs @@ -0,0 +1,137 @@ +import * as core from '@actions/core'; +import bytes from 'bytes'; + +const SIZE_RESULTS_HEADER = ['Path', 'Size', '% Change', 'Change']; + +const EmptyResult = { + name: '-', + size: 0, +}; + +export class SizeLimitFormatter { + formatBytes(size) { + return bytes.format(size, { unitSeparator: ' ' }); + } + + formatName(name, sizeLimit, passed) { + if (passed) { + return name; + } + + return `⛔️ ${name} (max: ${this.formatBytes(sizeLimit)})`; + } + + formatPercentageChange(base = 0, current = 0) { + if (base === 0) { + return 'added'; + } + + if (current === 0) { + return 'removed'; + } + + const value = ((current - base) / base) * 100; + const formatted = (Math.sign(value) * Math.ceil(Math.abs(value) * 100)) / 100; + + if (value > 0) { + return `+${formatted}%`; + } + + if (value === 0) { + return '-'; + } + + return `${formatted}%`; + } + + formatChange(base = 0, current = 0) { + if (base === 0) { + return 'added'; + } + + if (current === 0) { + return 'removed'; + } + + const value = current - base; + const formatted = this.formatBytes(value); + + if (value > 0) { + return `+${formatted} 🔺`; + } + + if (value === 0) { + return '-'; + } + + return `${formatted} 🔽`; + } + + formatLine(value, change) { + return `${value} (${change})`; + } + + formatSizeResult(name, base, current) { + if (!current.passed) { + core.debug( + `Size limit exceeded for ${name} - ${this.formatBytes(current.size)} > ${this.formatBytes(current.sizeLimit)}`, + ); + } + + return [ + this.formatName(name, current.sizeLimit, current.passed), + this.formatBytes(current.size), + this.formatPercentageChange(base.size, current.size), + this.formatChange(base.size, current.size), + ]; + } + + parseResults(output) { + const results = JSON.parse(output); + + return results.reduce((current, result) => { + return { + // biome-ignore lint/performance/noAccumulatingSpread: + ...current, + [result.name]: { + name: result.name, + size: +result.size, + sizeLimit: +result.sizeLimit, + passed: result.passed || false, + }, + }; + }, {}); + } + + hasSizeChanges(base, current, threshold = 0) { + if (!base || !current) { + return true; + } + + const names = [...new Set([...Object.keys(base), ...Object.keys(current)])]; + + return names.some(name => { + const baseResult = base[name] || EmptyResult; + const currentResult = current[name] || EmptyResult; + + if (!baseResult.size || !currentResult.size) { + return true; + } + + return Math.abs((currentResult.size - baseResult.size) / baseResult.size) * 100 > threshold; + }); + } + + formatResults(base, current) { + const names = [...new Set([...(base ? Object.keys(base) : []), ...Object.keys(current)])]; + const header = SIZE_RESULTS_HEADER; + const fields = names.map(name => { + const baseResult = base?.[name] || EmptyResult; + const currentResult = current[name] || EmptyResult; + + return this.formatSizeResult(name, baseResult, currentResult); + }); + + return [header, ...fields]; + } +} diff --git a/dev-packages/size-limit-gh-action/utils/getArtifactsForBranchAndWorkflow.mjs b/dev-packages/size-limit-gh-action/utils/getArtifactsForBranchAndWorkflow.mjs new file mode 100644 index 000000000000..6d512b46afe1 --- /dev/null +++ b/dev-packages/size-limit-gh-action/utils/getArtifactsForBranchAndWorkflow.mjs @@ -0,0 +1,128 @@ +import * as core from '@actions/core'; + +// max pages of workflows to pagination through +const DEFAULT_MAX_PAGES = 50; +// max results per page +const DEFAULT_PAGE_LIMIT = 10; + +/** + * Fetch artifacts from a workflow run from a branch + * + * This is a bit hacky since GitHub Actions currently does not directly + * support downloading artifacts from other workflows + */ +/** + * Fetch artifacts from a workflow run from a branch + * + * This is a bit hacky since GitHub Actions currently does not directly + * support downloading artifacts from other workflows + */ +export async function getArtifactsForBranchAndWorkflow(octokit, { owner, repo, workflowName, branch, artifactName }) { + let repositoryWorkflow = null; + + // For debugging + const allWorkflows = []; + + // + // Find workflow id from `workflowName` + // + for await (const response of octokit.paginate.iterator(octokit.rest.actions.listRepoWorkflows, { + owner, + repo, + })) { + const targetWorkflow = response.data.find(({ name }) => name === workflowName); + + allWorkflows.push(...response.data.map(({ name }) => name)); + + // If not found in responses, continue to search on next page + if (!targetWorkflow) { + continue; + } + + repositoryWorkflow = targetWorkflow; + break; + } + + if (!repositoryWorkflow) { + core.info( + `Unable to find workflow with name "${workflowName}" in the repository. Found workflows: ${allWorkflows.join( + ', ', + )}`, + ); + return null; + } + + const workflow_id = repositoryWorkflow.id; + + let currentPage = 0; + let latestWorkflowRun = null; + + for await (const response of octokit.paginate.iterator(octokit.rest.actions.listWorkflowRuns, { + owner, + repo, + workflow_id, + branch, + per_page: DEFAULT_PAGE_LIMIT, + event: 'push', + })) { + if (!response.data.length) { + core.warning(`Workflow ${workflow_id} not found in branch ${branch}`); + return null; + } + + // Do not allow downloading artifacts from a fork. + const filtered = response.data.filter(workflowRun => workflowRun.head_repository.full_name === `${owner}/${repo}`); + + // Sort to ensure the latest workflow run is the first + filtered.sort((a, b) => { + return new Date(b.created_at).getTime() - new Date(a.created_at).getTime(); + }); + + // Store the first workflow run, to determine if this is the latest one... + if (!latestWorkflowRun) { + latestWorkflowRun = filtered[0]; + } + + // Search through workflow artifacts until we find a workflow run w/ artifact name that we are looking for + for (const workflowRun of filtered) { + core.info(`Checking artifacts for workflow run: ${workflowRun.html_url}`); + + const { + data: { artifacts }, + } = await octokit.rest.actions.listWorkflowRunArtifacts({ + owner, + repo, + run_id: workflowRun.id, + }); + + if (!artifacts) { + core.warning( + `Unable to fetch artifacts for branch: ${branch}, workflow: ${workflow_id}, workflowRunId: ${workflowRun.id}`, + ); + } else { + const foundArtifact = artifacts.find(({ name }) => name === artifactName); + if (foundArtifact) { + core.info(`Found suitable artifact: ${foundArtifact.url}`); + return { + artifact: foundArtifact, + workflowRun, + isLatest: latestWorkflowRun.id === workflowRun.id, + }; + } else { + core.info(`No artifact found for ${artifactName}, trying next workflow run...`); + } + } + } + + if (currentPage > DEFAULT_MAX_PAGES) { + core.warning(`Workflow ${workflow_id} not found in branch: ${branch}`); + return null; + } + + currentPage++; + } + + core.warning(`Artifact not found: ${artifactName}`); + core.endGroup(); + return null; +} From f5ef4705d513d18f5b1e68932d9d704ea322c708 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:06:21 +0000 Subject: [PATCH 57/64] ref: Add external contributor to CHANGELOG.md (#13569) This PR adds the external contributor to the CHANGELOG.md file, so that they are credited for their contribution. See #13465 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 015c5370a75a..234c9e5704a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +Work in this release was contributed by @Zen-cronic. Thank you for your contribution! + ## 8.28.0 ### Important Changes From d5f9f9da8d6dd1bd203a95ccbae2bfe4559553a0 Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:04:51 -0400 Subject: [PATCH 58/64] fix(replay): Fix types in WebVitalData (#13573) Fixes attribution type: when adding an attribution in CLS we add {`value`, `nodeIds`} not {`value`, `sources`} --- packages/replay-internal/src/types/performance.ts | 2 +- packages/replay-internal/src/util/createPerformanceEntries.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/replay-internal/src/types/performance.ts b/packages/replay-internal/src/types/performance.ts index 7a60e51684f3..b3dcab0e7dd7 100644 --- a/packages/replay-internal/src/types/performance.ts +++ b/packages/replay-internal/src/types/performance.ts @@ -114,7 +114,7 @@ export interface WebVitalData { /** * The layout shifts of a CLS metric */ - attributions?: { value: number; sources?: number[] }[]; + attributions?: { value: number; nodeIds?: number[] }[]; } /** diff --git a/packages/replay-internal/src/util/createPerformanceEntries.ts b/packages/replay-internal/src/util/createPerformanceEntries.ts index 830f878dc8ea..0c22ba73163a 100644 --- a/packages/replay-internal/src/util/createPerformanceEntries.ts +++ b/packages/replay-internal/src/util/createPerformanceEntries.ts @@ -198,7 +198,7 @@ export function getLargestContentfulPaint(metric: Metric): ReplayPerformanceEntr return getWebVital(metric, 'largest-contentful-paint', node); } -function isLayoutShift(entry: PerformanceEntry | LayoutShift): entry is LayoutShift { +function isLayoutShift(entry: PerformanceEntry): entry is LayoutShift { return (entry as LayoutShift).sources !== undefined; } From 2393064cce4ae9335296c1a4e2d3131473b6feee Mon Sep 17 00:00:00 2001 From: Andrei <168741329+andreiborza@users.noreply.github.com> Date: Wed, 4 Sep 2024 00:39:28 +0200 Subject: [PATCH 59/64] chore(ci): Retain and upload traces on e2e-tests failures (#13562) --- .github/workflows/build.yml | 9 +++++++++ dev-packages/test-utils/src/playwright-config.ts | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4f06228f3a7e..77ddae4705b6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1003,6 +1003,15 @@ jobs: working-directory: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }} timeout-minutes: 5 run: pnpm test:assert + + - name: Upload Playwright Traces + uses: actions/upload-artifact@v4 + if: failure() + with: + name: playwright-traces-job_e2e_playwright_tests-${{ matrix.test-application}} + path: dev-packages/e2e-tests/test-applications/${{ matrix.test-application}}/test-results + overwrite: true + retention-days: 7 job_optional_e2e_tests: name: E2E ${{ matrix.label || matrix.test-application }} Test diff --git a/dev-packages/test-utils/src/playwright-config.ts b/dev-packages/test-utils/src/playwright-config.ts index da2a10d0b477..d30c8cad4475 100644 --- a/dev-packages/test-utils/src/playwright-config.ts +++ b/dev-packages/test-utils/src/playwright-config.ts @@ -46,7 +46,7 @@ export function getPlaywrightConfig( baseURL: `http://localhost:${appPort}`, /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', + trace: 'retain-on-failure', }, /* Configure projects for major browsers */ From d929462e250120796097ed7a802e1dd14858a882 Mon Sep 17 00:00:00 2001 From: Andrei <168741329+andreiborza@users.noreply.github.com> Date: Wed, 4 Sep 2024 00:40:19 +0200 Subject: [PATCH 60/64] feat(solidstart): Add `browserTracingIntegration` by default (#13561) Closes: #13352 --- packages/solidstart/src/client/sdk.ts | 29 ++++++++++++- packages/solidstart/test/client/sdk.test.ts | 45 +++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/packages/solidstart/src/client/sdk.ts b/packages/solidstart/src/client/sdk.ts index f44a2134ce50..8b1b6f7f39d5 100644 --- a/packages/solidstart/src/client/sdk.ts +++ b/packages/solidstart/src/client/sdk.ts @@ -1,13 +1,21 @@ import { applySdkMetadata } from '@sentry/core'; import type { BrowserOptions } from '@sentry/solid'; -import { init as initSolidSDK } from '@sentry/solid'; -import type { Client } from '@sentry/types'; +import { + browserTracingIntegration, + getDefaultIntegrations as getDefaultSolidIntegrations, + init as initSolidSDK, +} from '@sentry/solid'; +import type { Client, Integration } from '@sentry/types'; + +// Treeshakable guard to remove all code related to tracing +declare const __SENTRY_TRACING__: boolean; /** * Initializes the client side of the Solid Start SDK. */ export function init(options: BrowserOptions): Client | undefined { const opts = { + defaultIntegrations: getDefaultIntegrations(options), ...options, }; @@ -15,3 +23,20 @@ export function init(options: BrowserOptions): Client | undefined { return initSolidSDK(opts); } + +function getDefaultIntegrations(options: BrowserOptions): Integration[] { + const integrations = getDefaultSolidIntegrations(options); + + // This evaluates to true unless __SENTRY_TRACING__ is text-replaced with "false", + // in which case everything inside will get tree-shaken away + if (typeof __SENTRY_TRACING__ === 'undefined' || __SENTRY_TRACING__) { + // We add the default BrowserTracingIntegration here always. + // We can do this, even if `solidRouterBrowserTracingIntegration` is + // supplied as integration in `init` by users because it will win + // over the default integration by virtue of having the same + // `BrowserTracing` integration name and being added later. + integrations.push(browserTracingIntegration()); + } + + return integrations; +} diff --git a/packages/solidstart/test/client/sdk.test.ts b/packages/solidstart/test/client/sdk.test.ts index 886bb29b515d..b242e03ceb70 100644 --- a/packages/solidstart/test/client/sdk.test.ts +++ b/packages/solidstart/test/client/sdk.test.ts @@ -3,6 +3,7 @@ import * as SentrySolid from '@sentry/solid'; import { vi } from 'vitest'; import { init as solidStartInit } from '../../src/client'; +import { solidRouterBrowserTracingIntegration } from '../../src/client/solidrouter'; const browserInit = vi.spyOn(SentrySolid, 'init'); @@ -34,3 +35,47 @@ describe('Initialize Solid Start SDK', () => { expect(browserInit).toHaveBeenLastCalledWith(expect.objectContaining(expectedMetadata)); }); }); + +describe('browserTracingIntegration', () => { + it('adds the `browserTracingIntegration` when `__SENTRY_TRACING__` is not set', () => { + const client = solidStartInit({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }); + + const browserTracingIntegration = client + ?.getOptions() + .integrations.find(integration => integration.name === 'BrowserTracing'); + expect(browserTracingIntegration).toBeDefined(); + expect(browserTracingIntegration!.isDefaultInstance).toEqual(true); + }); + + it("doesn't add the `browserTracingIntegration` if `__SENTRY_TRACING__` is false", () => { + // @ts-expect-error Test setup for build-time flag + globalThis.__SENTRY_TRACING__ = false; + + const client = solidStartInit({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }); + + const browserTracingIntegration = client + ?.getOptions() + .integrations.find(integration => integration.name === 'BrowserTracing'); + expect(browserTracingIntegration).toBeUndefined(); + + // @ts-expect-error Test setup for build-time flag + delete globalThis.__SENTRY_TRACING__; + }); + + it("doesn't add the default `browserTracingIntegration` if `solidBrowserTracingIntegration` was already passed in", () => { + const client = solidStartInit({ + integrations: [solidRouterBrowserTracingIntegration()], + dsn: 'https://public@dsn.ingest.sentry.io/1337', + }); + + const browserTracingIntegration = client + ?.getOptions() + .integrations.find(integration => integration.name === 'BrowserTracing'); + expect(browserTracingIntegration).toBeDefined(); + expect(browserTracingIntegration!.isDefaultInstance).toBeUndefined(); + }); +}); From 5c05bd829f9b118ad5b2477ec149f489088e68f2 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 4 Sep 2024 09:57:09 +0200 Subject: [PATCH 61/64] fix(utils): Keep logger on carrier (#13570) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Today, if you have a pluggable integration, it will incorrectly use it's own logger, which will generally be disabled. This is because we inline all the logger code into the bundle, but the logger is never enabled. Now, the logger is kept as a singleton on the carrier, ensuring that we always use the same logger. I added browser integration tests to cover this - especially one in feedback which failed before. NOTE: This is still not really ideal, because we still include the whole `makeLogger` code in each pluggable integration that uses the logger 🤔 but we can look into this later - now, at least logging should work for pluggable integrations. --- .../suites/feedback/attachTo/init.js | 17 ++++ .../suites/feedback/attachTo/template.html | 9 ++ .../suites/feedback/attachTo/test.ts | 82 +++++++++++++++++++ .../suites/feedback/logger/init.js | 19 +++++ .../suites/feedback/logger/test.ts | 36 ++++++++ .../httpclient/httpClientIntegration/init.js | 11 --- .../httpClientIntegration/subject.js | 8 -- .../httpclient/httpClientIntegration/test.ts | 64 --------------- .../suites/replay/logger/init.js | 18 ++++ .../suites/replay/logger/test.ts | 36 ++++++++ .../utils/generatePlugin.ts | 1 - packages/utils/src/logger.ts | 8 +- packages/utils/src/worldwide.ts | 2 + 13 files changed, 225 insertions(+), 86 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/feedback/attachTo/init.js create mode 100644 dev-packages/browser-integration-tests/suites/feedback/attachTo/template.html create mode 100644 dev-packages/browser-integration-tests/suites/feedback/attachTo/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/feedback/logger/init.js create mode 100644 dev-packages/browser-integration-tests/suites/feedback/logger/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/replay/logger/init.js create mode 100644 dev-packages/browser-integration-tests/suites/replay/logger/test.ts diff --git a/dev-packages/browser-integration-tests/suites/feedback/attachTo/init.js b/dev-packages/browser-integration-tests/suites/feedback/attachTo/init.js new file mode 100644 index 000000000000..5eb27143fdc7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/attachTo/init.js @@ -0,0 +1,17 @@ +import * as Sentry from '@sentry/browser'; +// Import this separately so that generatePlugin can handle it for CDN scenarios +import { feedbackIntegration } from '@sentry/browser'; + +const feedback = feedbackIntegration({ + autoInject: false, +}); + +window.Sentry = Sentry; +window.feedback = feedback; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [feedback], +}); + +feedback.attachTo('#custom-feedback-buttom'); diff --git a/dev-packages/browser-integration-tests/suites/feedback/attachTo/template.html b/dev-packages/browser-integration-tests/suites/feedback/attachTo/template.html new file mode 100644 index 000000000000..ae36b0c69c7b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/attachTo/template.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/feedback/attachTo/test.ts b/dev-packages/browser-integration-tests/suites/feedback/attachTo/test.ts new file mode 100644 index 000000000000..507b08685092 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/attachTo/test.ts @@ -0,0 +1,82 @@ +import { expect } from '@playwright/test'; + +import { TEST_HOST, sentryTest } from '../../../utils/fixtures'; +import { envelopeRequestParser, getEnvelopeType, shouldSkipFeedbackTest } from '../../../utils/helpers'; + +sentryTest('should capture feedback with custom button', async ({ getLocalTestUrl, page }) => { + if (shouldSkipFeedbackTest()) { + sentryTest.skip(); + } + + const feedbackRequestPromise = page.waitForResponse(res => { + const req = res.request(); + + const postData = req.postData(); + if (!postData) { + return false; + } + + try { + return getEnvelopeType(req) === 'feedback'; + } catch (err) { + return false; + } + }); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + await page.locator('#custom-feedback-buttom').click(); + await page.waitForSelector(':visible:text-is("Report a Bug")'); + + expect(await page.locator(':visible:text-is("Report a Bug")').count()).toEqual(1); + await page.locator('[name="name"]').fill('Jane Doe'); + await page.locator('[name="email"]').fill('janedoe@example.org'); + await page.locator('[name="message"]').fill('my example feedback'); + await page.locator('[data-sentry-feedback] .btn--primary').click(); + + const feedbackEvent = envelopeRequestParser((await feedbackRequestPromise).request()); + expect(feedbackEvent).toEqual({ + type: 'feedback', + breadcrumbs: expect.any(Array), + contexts: { + feedback: { + contact_email: 'janedoe@example.org', + message: 'my example feedback', + name: 'Jane Doe', + source: 'widget', + url: `${TEST_HOST}/index.html`, + }, + trace: { + trace_id: expect.stringMatching(/\w{32}/), + span_id: expect.stringMatching(/\w{16}/), + }, + }, + level: 'info', + timestamp: expect.any(Number), + event_id: expect.stringMatching(/\w{32}/), + environment: 'production', + tags: {}, + sdk: { + integrations: expect.arrayContaining(['Feedback']), + version: expect.any(String), + name: 'sentry.javascript.browser', + packages: expect.anything(), + }, + request: { + url: `${TEST_HOST}/index.html`, + headers: { + 'User-Agent': expect.stringContaining(''), + }, + }, + platform: 'javascript', + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/feedback/logger/init.js b/dev-packages/browser-integration-tests/suites/feedback/logger/init.js new file mode 100644 index 000000000000..3251bd6c7a4c --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/logger/init.js @@ -0,0 +1,19 @@ +import * as Sentry from '@sentry/browser'; +// Import this separately so that generatePlugin can handle it for CDN scenarios +import { feedbackIntegration } from '@sentry/browser'; + +const feedback = feedbackIntegration({ + autoInject: false, +}); + +window.Sentry = Sentry; +window.feedback = feedback; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + debug: true, + integrations: [feedback], +}); + +// This should log an error! +feedback.attachTo('#does-not-exist'); diff --git a/dev-packages/browser-integration-tests/suites/feedback/logger/test.ts b/dev-packages/browser-integration-tests/suites/feedback/logger/test.ts new file mode 100644 index 000000000000..34fadfc2503b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/logger/test.ts @@ -0,0 +1,36 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../utils/fixtures'; +import { shouldSkipFeedbackTest } from '../../../utils/helpers'; + +/** + * This test is mostly relevant for ensuring that the logger works in all combinations of CDN bundles. + * Even if feedback is included via the CDN, this test ensures that the logger is working correctly. + */ +sentryTest('should log error correctly', async ({ getLocalTestUrl, page }) => { + // In minified bundles we do not have logger messages, so we skip the test + if (shouldSkipFeedbackTest() || (process.env.PW_BUNDLE || '').includes('_min')) { + sentryTest.skip(); + } + + const messages: string[] = []; + + page.on('console', message => { + messages.push(message.text()); + }); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + + expect(messages).toContain('Sentry Logger [log]: Integration installed: Feedback'); + expect(messages).toContain('Sentry Logger [error]: [Feedback] Unable to attach to target element'); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/init.js b/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/init.js deleted file mode 100644 index 8540ab176c38..000000000000 --- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/init.js +++ /dev/null @@ -1,11 +0,0 @@ -import * as Sentry from '@sentry/browser'; -import { httpClientIntegration } from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [httpClientIntegration()], - tracesSampleRate: 1, - sendDefaultPii: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/subject.js deleted file mode 100644 index 563b069e66cc..000000000000 --- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/subject.js +++ /dev/null @@ -1,8 +0,0 @@ -const xhr = new XMLHttpRequest(); - -xhr.open('GET', 'http://sentry-test.io/foo', true); -xhr.withCredentials = true; -xhr.setRequestHeader('Accept', 'application/json'); -xhr.setRequestHeader('Content-Type', 'application/json'); -xhr.setRequestHeader('Cache', 'no-cache'); -xhr.send(); diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/test.ts deleted file mode 100644 index f064a8652b48..000000000000 --- a/dev-packages/browser-integration-tests/suites/integrations/httpclient/httpClientIntegration/test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { expect } from '@playwright/test'; -import type { Event } from '@sentry/types'; - -import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; - -sentryTest('works with httpClientIntegration', async ({ getLocalTestPath, page }) => { - const url = await getLocalTestPath({ testDir: __dirname }); - - await page.route('**/foo', route => { - return route.fulfill({ - status: 500, - body: JSON.stringify({ - error: { - message: 'Internal Server Error', - }, - }), - headers: { - 'Content-Type': 'text/html', - }, - }); - }); - - const eventData = await getFirstSentryEnvelopeRequest(page, url); - - expect(eventData.exception?.values).toHaveLength(1); - - // Not able to get the cookies from the request/response because of Playwright bug - // https://github.com/microsoft/playwright/issues/11035 - expect(eventData).toMatchObject({ - message: 'HTTP Client Error with status code: 500', - exception: { - values: [ - { - type: 'Error', - value: 'HTTP Client Error with status code: 500', - mechanism: { - type: 'http.client', - handled: false, - }, - }, - ], - }, - request: { - url: 'http://sentry-test.io/foo', - method: 'GET', - headers: { - accept: 'application/json', - cache: 'no-cache', - 'content-type': 'application/json', - }, - }, - contexts: { - response: { - status_code: 500, - body_size: 45, - headers: { - 'content-type': 'text/html', - 'content-length': '45', - }, - }, - }, - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/replay/logger/init.js b/dev-packages/browser-integration-tests/suites/replay/logger/init.js new file mode 100644 index 000000000000..195be16ddad3 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/replay/logger/init.js @@ -0,0 +1,18 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window.Replay = Sentry.replayIntegration({ + flushMinDelay: 200, + flushMaxDelay: 200, + minReplayDuration: 0, +}); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 0, + replaysSessionSampleRate: 1.0, + replaysOnErrorSampleRate: 0.0, + debug: true, + + integrations: [window.Replay], +}); diff --git a/dev-packages/browser-integration-tests/suites/replay/logger/test.ts b/dev-packages/browser-integration-tests/suites/replay/logger/test.ts new file mode 100644 index 000000000000..fa034a12b003 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/replay/logger/test.ts @@ -0,0 +1,36 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../utils/fixtures'; +import { shouldSkipReplayTest, waitForReplayRequest } from '../../../utils/replayHelpers'; + +sentryTest('should output logger messages', async ({ getLocalTestPath, page }) => { + // In minified bundles we do not have logger messages, so we skip the test + if (shouldSkipReplayTest() || (process.env.PW_BUNDLE || '').includes('_min')) { + sentryTest.skip(); + } + + const messages: string[] = []; + + page.on('console', message => { + messages.push(message.text()); + }); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const reqPromise0 = waitForReplayRequest(page, 0); + + const url = await getLocalTestPath({ testDir: __dirname }); + + await Promise.all([page.goto(url), reqPromise0]); + + expect(messages).toContain('Sentry Logger [log]: Integration installed: Replay'); + expect(messages).toContain('Sentry Logger [info]: [Replay] Creating new session'); + expect(messages).toContain('Sentry Logger [info]: [Replay] Starting replay in session mode'); + expect(messages).toContain('Sentry Logger [info]: [Replay] Using compression worker'); +}); diff --git a/dev-packages/browser-integration-tests/utils/generatePlugin.ts b/dev-packages/browser-integration-tests/utils/generatePlugin.ts index 9189ce63812f..acc583506df4 100644 --- a/dev-packages/browser-integration-tests/utils/generatePlugin.ts +++ b/dev-packages/browser-integration-tests/utils/generatePlugin.ts @@ -30,7 +30,6 @@ const useLoader = bundleKey.startsWith('loader'); const IMPORTED_INTEGRATION_CDN_BUNDLE_PATHS: Record = { httpClientIntegration: 'httpclient', captureConsoleIntegration: 'captureconsole', - CaptureConsole: 'captureconsole', debugIntegration: 'debug', rewriteFramesIntegration: 'rewriteframes', contextLinesIntegration: 'contextlines', diff --git a/packages/utils/src/logger.ts b/packages/utils/src/logger.ts index e996d87202b2..533b59fd5882 100644 --- a/packages/utils/src/logger.ts +++ b/packages/utils/src/logger.ts @@ -1,7 +1,7 @@ import type { ConsoleLevel } from '@sentry/types'; import { DEBUG_BUILD } from './debug-build'; -import { GLOBAL_OBJ } from './worldwide'; +import { GLOBAL_OBJ, getGlobalSingleton } from './worldwide'; /** Prefix for logging strings */ const PREFIX = 'Sentry Logger '; @@ -97,4 +97,8 @@ function makeLogger(): Logger { return logger as Logger; } -export const logger = makeLogger(); +/** + * This is a logger singleton which either logs things or no-ops if logging is not enabled. + * The logger is a singleton on the carrier, to ensure that a consistent logger is used throughout the SDK. + */ +export const logger = getGlobalSingleton('logger', makeLogger); diff --git a/packages/utils/src/worldwide.ts b/packages/utils/src/worldwide.ts index e323f12034a2..2a1ca7b958d8 100644 --- a/packages/utils/src/worldwide.ts +++ b/packages/utils/src/worldwide.ts @@ -15,6 +15,7 @@ import type { Client, MetricsAggregator, Scope } from '@sentry/types'; import type { SdkSource } from './env'; +import type { logger } from './logger'; import { SDK_VERSION } from './version'; interface SentryCarrier { @@ -25,6 +26,7 @@ interface SentryCarrier { defaultIsolationScope?: Scope; defaultCurrentScope?: Scope; globalMetricsAggregators?: WeakMap | undefined; + logger?: typeof logger; /** Overwrites TextEncoder used in `@sentry/utils`, need for `react-native@0.73` and older */ encodePolyfill?: (input: string) => Uint8Array; From ec7d9e3d632fc7b805771f6e8d08c0cb9a2c0349 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 4 Sep 2024 11:08:08 +0200 Subject: [PATCH 62/64] ci: Update size-limit to ensure bundle size optimization (#13571) I wanted to add a new entry for size limit with sentry browser with max. tree shaking. While doing this, I noticed that bundle size did not shrink as much as I would have expected it. After playing around some, I noticed that it was not actually shaking out the constant changes, for whatever reason. Probably some size-limit internal default config. Now, i actually add terser to ensure it fully minimizes & tree shakes stuff for us. --- .size-limit.js | 37 ++++ .../browser-integration-tests/package.json | 2 +- .../bundle-analyzer-scenarios/package.json | 2 +- packages/ember/package.json | 2 +- packages/nextjs/package.json | 2 +- yarn.lock | 197 ++---------------- 6 files changed, 56 insertions(+), 186 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index d2211d91d5b0..5da293511976 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -10,6 +10,31 @@ module.exports = [ gzip: true, limit: '24 KB', }, + { + name: '@sentry/browser - with treeshaking flags', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init'), + gzip: true, + limit: '24 KB', + modifyWebpackConfig: function (config) { + const webpack = require('webpack'); + const TerserPlugin = require('terser-webpack-plugin'); + + config.plugins.push( + new webpack.DefinePlugin({ + __SENTRY_DEBUG__: false, + __RRWEB_EXCLUDE_SHADOW_DOM__: true, + __RRWEB_EXCLUDE_IFRAME__: true, + __SENTRY_EXCLUDE_REPLAY_WORKER__: true, + }), + ); + + config.optimization.minimize = true; + config.optimization.minimizer = [new TerserPlugin()]; + + return config; + }, + }, { name: '@sentry/browser (incl. Tracing)', path: 'packages/browser/build/npm/esm/index.js', @@ -32,6 +57,8 @@ module.exports = [ limit: '68 KB', modifyWebpackConfig: function (config) { const webpack = require('webpack'); + const TerserPlugin = require('terser-webpack-plugin'); + config.plugins.push( new webpack.DefinePlugin({ __SENTRY_DEBUG__: false, @@ -40,6 +67,10 @@ module.exports = [ __SENTRY_EXCLUDE_REPLAY_WORKER__: true, }), ); + + config.optimization.minimize = true; + config.optimization.minimizer = [new TerserPlugin()]; + return config; }, }, @@ -222,11 +253,17 @@ module.exports = [ ignore: [...builtinModules, ...nodePrefixedBuiltinModules], modifyWebpackConfig: function (config) { const webpack = require('webpack'); + const TerserPlugin = require('terser-webpack-plugin'); + config.plugins.push( new webpack.DefinePlugin({ __SENTRY_TRACING__: false, }), ); + + config.optimization.minimize = true; + config.optimization.minimizer = [new TerserPlugin()]; + return config; }, }, diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index 886f91d328fb..d012484a4e0b 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -48,7 +48,7 @@ "babel-loader": "^8.2.2", "html-webpack-plugin": "^5.5.0", "pako": "^2.1.0", - "webpack": "^5.90.3" + "webpack": "^5.94.0" }, "devDependencies": { "@types/glob": "8.0.0", diff --git a/dev-packages/bundle-analyzer-scenarios/package.json b/dev-packages/bundle-analyzer-scenarios/package.json index 6cec8aa83b2f..b1375418e16f 100644 --- a/dev-packages/bundle-analyzer-scenarios/package.json +++ b/dev-packages/bundle-analyzer-scenarios/package.json @@ -9,7 +9,7 @@ "private": true, "dependencies": { "html-webpack-plugin": "^5.6.0", - "webpack": "^5.92.1", + "webpack": "^5.94.0", "webpack-bundle-analyzer": "^4.10.2" }, "scripts": { diff --git a/packages/ember/package.json b/packages/ember/package.json index 2ff37964532e..1bec2d30f9bf 100644 --- a/packages/ember/package.json +++ b/packages/ember/package.json @@ -80,7 +80,7 @@ "qunit": "~2.19.2", "qunit-dom": "~2.0.0", "sinon": "15.2.0", - "webpack": "~5.90.3" + "webpack": "~5.94.0" }, "engines": { "node": ">=14.18" diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 6965ddcf5167..354870f6e834 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -91,7 +91,7 @@ }, "peerDependencies": { "next": "^13.2.0 || ^14.0 || ^15.0.0-rc.0", - "webpack": ">= 5.0.0" + "webpack": "5.94.0" }, "peerDependenciesMeta": { "webpack": { diff --git a/yarn.lock b/yarn.lock index 1acd7e11fbd9..b0a8c712e374 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9681,17 +9681,7 @@ dependencies: "@types/unist" "*" -"@types/history-4@npm:@types/history@4.7.8": - version "4.7.8" - resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" - integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== - -"@types/history-5@npm:@types/history@4.7.8": - version "4.7.8" - resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" - integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== - -"@types/history@*": +"@types/history-4@npm:@types/history@4.7.8", "@types/history-5@npm:@types/history@4.7.8", "@types/history@*": version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== @@ -10012,15 +10002,7 @@ "@types/history" "^3" "@types/react" "*" -"@types/react-router-4@npm:@types/react-router@5.1.14": - version "5.1.14" - resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.14.tgz#e0442f4eb4c446541ad7435d44a97f8fe6df40da" - integrity sha512-LAJpqYUaCTMT2anZheoidiIymt8MuX286zoVFPM3DVb23aQBH0mAkFvzpd4LKqiolV8bBtZWT5Qp7hClCNDENw== - dependencies: - "@types/history" "*" - "@types/react" "*" - -"@types/react-router-5@npm:@types/react-router@5.1.14": +"@types/react-router-4@npm:@types/react-router@5.1.14", "@types/react-router-5@npm:@types/react-router@5.1.14": version "5.1.14" resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.14.tgz#e0442f4eb4c446541ad7435d44a97f8fe6df40da" integrity sha512-LAJpqYUaCTMT2anZheoidiIymt8MuX286zoVFPM3DVb23aQBH0mAkFvzpd4LKqiolV8bBtZWT5Qp7hClCNDENw== @@ -10931,14 +10913,6 @@ "@webassemblyjs/helper-numbers" "1.11.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.1" -"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" - integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== - dependencies: - "@webassemblyjs/helper-numbers" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" @@ -10972,11 +10946,6 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== -"@webassemblyjs/helper-buffer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" - integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== - "@webassemblyjs/helper-buffer@1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" @@ -11020,16 +10989,6 @@ "@webassemblyjs/helper-wasm-bytecode" "1.11.1" "@webassemblyjs/wasm-gen" "1.11.1" -"@webassemblyjs/helper-wasm-section@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" - integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/helper-wasm-section@1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" @@ -11092,20 +11051,6 @@ "@webassemblyjs/wasm-parser" "1.11.1" "@webassemblyjs/wast-printer" "1.11.1" -"@webassemblyjs/wasm-edit@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" - integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/helper-wasm-section" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-opt" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" - "@webassemblyjs/wast-printer" "1.11.6" - "@webassemblyjs/wasm-edit@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" @@ -11131,17 +11076,6 @@ "@webassemblyjs/leb128" "1.11.1" "@webassemblyjs/utf8" "1.11.1" -"@webassemblyjs/wasm-gen@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" - integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/ieee754" "1.11.6" - "@webassemblyjs/leb128" "1.11.6" - "@webassemblyjs/utf8" "1.11.6" - "@webassemblyjs/wasm-gen@1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" @@ -11163,16 +11097,6 @@ "@webassemblyjs/wasm-gen" "1.11.1" "@webassemblyjs/wasm-parser" "1.11.1" -"@webassemblyjs/wasm-opt@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" - integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" - "@webassemblyjs/wasm-opt@1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" @@ -11195,18 +11119,6 @@ "@webassemblyjs/leb128" "1.11.1" "@webassemblyjs/utf8" "1.11.1" -"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" - integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-api-error" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/ieee754" "1.11.6" - "@webassemblyjs/leb128" "1.11.6" - "@webassemblyjs/utf8" "1.11.6" - "@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" @@ -11227,14 +11139,6 @@ "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" -"@webassemblyjs/wast-printer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" - integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@xtuc/long" "4.2.2" - "@webassemblyjs/wast-printer@1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" @@ -16908,18 +16812,10 @@ enhanced-resolve@^5.14.1: graceful-fs "^4.2.4" tapable "^2.2.0" -enhanced-resolve@^5.15.0: - version "5.15.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" - integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - -enhanced-resolve@^5.17.0: - version "5.17.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" - integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== +enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -28522,7 +28418,7 @@ react-is@^18.0.0: dependencies: "@remix-run/router" "1.0.2" -"react-router-6@npm:react-router@6.3.0": +"react-router-6@npm:react-router@6.3.0", react-router@6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557" integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ== @@ -28537,13 +28433,6 @@ react-router-dom@^6.2.2: history "^5.2.0" react-router "6.3.0" -react-router@6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557" - integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ== - dependencies: - history "^5.2.0" - react@^18.0.0: version "18.0.0" resolved "https://registry.yarnpkg.com/react/-/react-18.0.0.tgz#b468736d1f4a5891f38585ba8e8fb29f91c3cb96" @@ -31022,16 +30911,7 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@4.2.3, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@4.2.3, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -31143,14 +31023,7 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -33810,42 +33683,11 @@ webpack@5.76.1: watchpack "^2.4.0" webpack-sources "^3.2.3" -webpack@^5.90.3, webpack@~5.90.3: - version "5.90.3" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.90.3.tgz#37b8f74d3ded061ba789bb22b31e82eed75bd9ac" - integrity sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA== +webpack@^5.90.3, webpack@^5.94.0, webpack@~5.94.0: + version "5.94.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f" + integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg== dependencies: - "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.5" - "@webassemblyjs/ast" "^1.11.5" - "@webassemblyjs/wasm-edit" "^1.11.5" - "@webassemblyjs/wasm-parser" "^1.11.5" - acorn "^8.7.1" - acorn-import-assertions "^1.9.0" - browserslist "^4.21.10" - chrome-trace-event "^1.0.2" - enhanced-resolve "^5.15.0" - es-module-lexer "^1.2.1" - eslint-scope "5.1.1" - events "^3.2.0" - glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" - json-parse-even-better-errors "^2.3.1" - loader-runner "^4.2.0" - mime-types "^2.1.27" - neo-async "^2.6.2" - schema-utils "^3.2.0" - tapable "^2.1.1" - terser-webpack-plugin "^5.3.10" - watchpack "^2.4.0" - webpack-sources "^3.2.3" - -webpack@^5.92.1: - version "5.92.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.92.1.tgz#eca5c1725b9e189cffbd86e8b6c3c7400efc5788" - integrity sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA== - dependencies: - "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.5" "@webassemblyjs/ast" "^1.12.1" "@webassemblyjs/wasm-edit" "^1.12.1" @@ -33854,7 +33696,7 @@ webpack@^5.92.1: acorn-import-attributes "^1.9.5" browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.17.0" + enhanced-resolve "^5.17.1" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" @@ -34150,16 +33992,7 @@ wrangler@^3.67.1: optionalDependencies: fsevents "~2.3.2" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@7.0.0, wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@7.0.0, wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 70f938b75ae868839bda632a1b4bf2cb9d6fab12 Mon Sep 17 00:00:00 2001 From: Sigrid Huemer <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 4 Sep 2024 12:04:27 +0200 Subject: [PATCH 63/64] fix(node-fetch): Use stringified origin url (#13581) Uses the stringified value of `origin` to call `.endsWith()`. closes https://github.com/getsentry/sentry-javascript/issues/13574 --- packages/node/src/integrations/node-fetch.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node/src/integrations/node-fetch.ts b/packages/node/src/integrations/node-fetch.ts index 093b314a6138..fa7a9974135a 100644 --- a/packages/node/src/integrations/node-fetch.ts +++ b/packages/node/src/integrations/node-fetch.ts @@ -192,11 +192,11 @@ function getBreadcrumbData(request: FetchRequest): Partial function getAbsoluteUrl(origin: string, path: string = '/'): string { const url = `${origin}`; - if (origin.endsWith('/') && path.startsWith('/')) { + if (url.endsWith('/') && path.startsWith('/')) { return `${url}${path.slice(1)}`; } - if (!origin.endsWith('/') && !path.startsWith('/')) { + if (!url.endsWith('/') && !path.startsWith('/')) { return `${url}/${path.slice(1)}`; } From 6dc8aab09431561c21e9016d6dcecbc9651b6ebd Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 4 Sep 2024 12:23:03 +0200 Subject: [PATCH 64/64] fix(cloudflare): Guard `context.waitUntil` call in request handler (#13549) Guard the call for `context.waitUntil` in the CF request handler wrapper. Our public API requires `context` to be present, however, in certain cases like Astro during build (prerendering), `context` becomes `undefined`. --- packages/cloudflare/src/request.ts | 10 ++++++++-- packages/cloudflare/test/request.test.ts | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/cloudflare/src/request.ts b/packages/cloudflare/src/request.ts index 560c17afb9e7..7a474c3b27cb 100644 --- a/packages/cloudflare/src/request.ts +++ b/packages/cloudflare/src/request.ts @@ -31,7 +31,13 @@ export function wrapRequestHandler( handler: (...args: unknown[]) => Response | Promise, ): Promise { return withIsolationScope(async isolationScope => { - const { options, request, context } = wrapperOptions; + const { options, request } = wrapperOptions; + + // In certain situations, the passed context can become undefined. + // For example, for Astro while prerendering pages at build time. + // see: https://github.com/getsentry/sentry-javascript/issues/13217 + const context = wrapperOptions.context as ExecutionContext | undefined; + const client = init(options); isolationScope.setClient(client); @@ -89,7 +95,7 @@ export function wrapRequestHandler( captureException(e, { mechanism: { handled: false, type: 'cloudflare' } }); throw e; } finally { - context.waitUntil(flush(2000)); + context?.waitUntil(flush(2000)); } }, ); diff --git a/packages/cloudflare/test/request.test.ts b/packages/cloudflare/test/request.test.ts index 93764a292ab4..5218e8afe20b 100644 --- a/packages/cloudflare/test/request.test.ts +++ b/packages/cloudflare/test/request.test.ts @@ -45,6 +45,15 @@ describe('withSentry', () => { expect(context.waitUntil).toHaveBeenLastCalledWith(expect.any(Promise)); }); + test("doesn't error if context is undefined", () => { + expect(() => + wrapRequestHandler( + { options: MOCK_OPTIONS, request: new Request('https://example.com'), context: undefined as any }, + () => new Response('test'), + ), + ).not.toThrow(); + }); + test('creates a cloudflare client and sets it on the handler', async () => { const initAndBindSpy = vi.spyOn(SentryCore, 'initAndBind'); await wrapRequestHandler(