diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 000000000000..8dfa2dec155e --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,14 @@ +{ + "permissions": { + "allow": [ + "Bash(find:*)", + "Bash(ls:*)", + "Bash(git:*)", + "Bash(yarn:*)", + "WebFetch(domain:github.com)", + "Bash(grep:*)", + "Bash(mv:*)" + ], + "deny": [] + } +} diff --git a/.craft.yml b/.craft.yml new file mode 100644 index 000000000000..c5055acf329c --- /dev/null +++ b/.craft.yml @@ -0,0 +1,230 @@ +minVersion: '0.23.1' +changelogPolicy: simple +preReleaseCommand: bash scripts/craft-pre-release.sh +targets: + # NPM Targets + ## 1. Base Packages, node or browser SDKs depend on + ## 1.1 Types + - name: npm + id: '@sentry/types' + includeNames: /^sentry-types-\d.*\.tgz$/ + ## 1.2 Core SDKs + - name: npm + id: '@sentry/core' + includeNames: /^sentry-core-\d.*\.tgz$/ + - name: npm + id: '@sentry/node-core' + includeNames: /^sentry-node-core-\d.*\.tgz$/ + ## 1.3 Browser Utils package + - name: npm + id: '@sentry-internal/browser-utils' + includeNames: /^sentry-internal-browser-utils-\d.*\.tgz$/ + ## 1.4 Replay Internal package (browser only) + - name: npm + id: '@sentry-internal/replay' + includeNames: /^sentry-internal-replay-\d.*\.tgz$/ + ## 1.5 OpenTelemetry package + - name: npm + id: '@sentry/opentelemetry' + includeNames: /^sentry-opentelemetry-\d.*\.tgz$/ + ## 1.6 Feedback package (browser only) + - name: npm + id: '@sentry-internal/feedback' + includeNames: /^sentry-internal-feedback-\d.*\.tgz$/ + ## 1.7 ReplayCanvas package (browser only) + - name: npm + id: '@sentry-internal/replay-canvas' + includeNames: /^sentry-internal-replay-canvas-\d.*\.tgz$/ + + ## 2. Browser & Node SDKs + - name: npm + id: '@sentry/browser' + includeNames: /^sentry-browser-\d.*\.tgz$/ + - name: npm + id: '@sentry/node' + includeNames: /^sentry-node-\d.*\.tgz$/ + - name: npm + id: '@sentry/profiling-node' + includeNames: /^sentry-profiling-node-\d.*\.tgz$/ + - name: npm + id: '@sentry/node-native' + includeNames: /^sentry-node-native-\d.*\.tgz$/ + + ## 3 Browser-based Packages + - name: npm + id: '@sentry/angular' + includeNames: /^sentry-angular-\d.*\.tgz$/ + - name: npm + id: '@sentry/ember' + includeNames: /^sentry-ember-\d.*\.tgz$/ + - name: npm + id: '@sentry/react' + includeNames: /^sentry-react-\d.*\.tgz$/ + - name: npm + id: '@sentry/solid' + includeNames: /^sentry-solid-\d.*\.tgz$/ + - name: npm + id: '@sentry/svelte' + includeNames: /^sentry-svelte-\d.*\.tgz$/ + - name: npm + id: '@sentry/vue' + includeNames: /^sentry-vue-\d.*\.tgz$/ + - name: npm + id: '@sentry/wasm' + includeNames: /^sentry-wasm-\d.*\.tgz$/ + + ## 4. WinterCG Packages + - name: npm + id: '@sentry/vercel-edge' + includeNames: /^sentry-vercel-edge-\d.*\.tgz$/ + - name: npm + id: '@sentry/cloudflare' + includeNames: /^sentry-cloudflare-\d.*\.tgz$/ + - name: npm + id: '@sentry/deno' + includeNames: /^sentry-deno-\d.*\.tgz$/ + + ## 5. Node-based Packages + - name: npm + id: '@sentry/aws-serverless' + includeNames: /^sentry-aws-serverless-\d.*\.tgz$/ + - name: npm + id: '@sentry/google-cloud-serverless' + includeNames: /^sentry-google-cloud-serverless-\d.*\.tgz$/ + - name: npm + id: '@sentry/bun' + includeNames: /^sentry-bun-\d.*\.tgz$/ + - name: npm + id: '@sentry/nestjs' + includeNames: /^sentry-nestjs-\d.*\.tgz$/ + + ## 6. Fullstack/Meta Frameworks (depending on Node and Browser or Framework SDKs) + - name: npm + id: '@sentry/nextjs' + includeNames: /^sentry-nextjs-\d.*\.tgz$/ + - name: npm + id: '@sentry/nuxt' + includeNames: /^sentry-nuxt-\d.*\.tgz$/ + - name: npm + id: '@sentry/remix' + includeNames: /^sentry-remix-\d.*\.tgz$/ + - name: npm + id: '@sentry/solidstart' + includeNames: /^sentry-solidstart-\d.*\.tgz$/ + - name: npm + id: '@sentry/sveltekit' + includeNames: /^sentry-sveltekit-\d.*\.tgz$/ + - name: npm + id: '@sentry/tanstackstart' + includeNames: /^sentry-tanstackstart-\d.*\.tgz$/ + - name: npm + id: '@sentry/tanstackstart-react' + includeNames: /^sentry-tanstackstart-react-\d.*\.tgz$/ + - name: npm + id: '@sentry/gatsby' + includeNames: /^sentry-gatsby-\d.*\.tgz$/ + - name: npm + id: '@sentry/astro' + includeNames: /^sentry-astro-\d.*\.tgz$/ + - name: npm + id: '@sentry/react-router' + includeNames: /^sentry-react-router-\d.*\.tgz$/ + + ## 7. Other Packages + ## 7.1 + - name: npm + id: '@sentry-internal/typescript' + includeNames: /^sentry-internal-typescript-\d.*\.tgz$/ + - name: npm + id: '@sentry-internal/eslint-plugin-sdk' + includeNames: /^sentry-internal-eslint-plugin-sdk-\d.*\.tgz$/ + ## 7.2 + - name: npm + id: '@sentry-internal/eslint-config-sdk' + includeNames: /^sentry-internal-eslint-config-sdk-\d.*\.tgz$/ + + # AWS Lambda Layer target + - name: aws-lambda-layer + includeNames: /^sentry-node-serverless-\d+.\d+.\d+(-(beta|alpha|rc)\.\d+)?\.zip$/ + layerName: SentryNodeServerlessSDKv10 + compatibleRuntimes: + - name: node + versions: + - nodejs18.x + - nodejs20.x + - nodejs22.x + license: MIT + + # CDN Bundle Target + - name: gcs + id: 'browser-cdn-bundles' + includeNames: /.*\.js.*$/ + bucket: sentry-js-sdk + paths: + - path: /{{version}}/ + metadata: + cacheControl: 'public, max-age=31536000' + + # Github Release Target + - name: github + includeNames: /^sentry-.*$/ + + # Sentry Release Registry Target + - name: registry + sdks: + 'npm:@sentry/angular': + onlyIfPresent: /^sentry-angular-\d.*\.tgz$/ + 'npm:@sentry/astro': + onlyIfPresent: /^sentry-astro-\d.*\.tgz$/ + 'npm:@sentry/aws-serverless': + onlyIfPresent: /^sentry-aws-serverless-\d.*\.tgz$/ + 'npm:@sentry/browser': + onlyIfPresent: /^sentry-browser-\d.*\.tgz$/ + includeNames: /\.js$/ + checksums: + - algorithm: sha384 + format: base64 + 'npm:@sentry/bun': + onlyIfPresent: /^sentry-bun-\d.*\.tgz$/ + 'npm:@sentry/cloudflare': + onlyIfPresent: /^sentry-cloudflare-\d.*\.tgz$/ + 'npm:@sentry/deno': + onlyIfPresent: /^sentry-deno-\d.*\.tgz$/ + 'npm:@sentry/ember': + onlyIfPresent: /^sentry-ember-\d.*\.tgz$/ + 'npm:@sentry/gatsby': + onlyIfPresent: /^sentry-gatsby-\d.*\.tgz$/ + 'npm:@sentry/google-cloud-serverless': + onlyIfPresent: /^sentry-google-cloud-serverless-\d.*\.tgz$/ + 'npm:@sentry/nestjs': + onlyIfPresent: /^sentry-nestjs-\d.*\.tgz$/ + 'npm:@sentry/nextjs': + onlyIfPresent: /^sentry-nextjs-\d.*\.tgz$/ + 'npm:@sentry/nuxt': + onlyIfPresent: /^sentry-nuxt-\d.*\.tgz$/ + 'npm:@sentry/node': + onlyIfPresent: /^sentry-node-\d.*\.tgz$/ + 'npm:@sentry/node-core': + onlyIfPresent: /^sentry-node-core-\d.*\.tgz$/ + 'npm:@sentry/react': + onlyIfPresent: /^sentry-react-\d.*\.tgz$/ + 'npm:@sentry/react-router': + onlyIfPresent: /^sentry-react-router-\d.*\.tgz$/ + 'npm:@sentry/remix': + onlyIfPresent: /^sentry-remix-\d.*\.tgz$/ + 'npm:@sentry/solid': + onlyIfPresent: /^sentry-solid-\d.*\.tgz$/ + 'npm:@sentry/solidstart': + onlyIfPresent: /^sentry-solidstart-\d.*\.tgz$/ + 'npm:@sentry/svelte': + onlyIfPresent: /^sentry-svelte-\d.*\.tgz$/ + 'npm:@sentry/sveltekit': + onlyIfPresent: /^sentry-sveltekit-\d.*\.tgz$/ + 'npm:@sentry/tanstackstart-react': + onlyIfPresent: /^sentry-tanstackstart-react-\d.*\.tgz$/ + 'npm:@sentry/vercel-edge': + onlyIfPresent: /^sentry-vercel-edge-\d.*\.tgz$/ + 'npm:@sentry/vue': + onlyIfPresent: /^sentry-vue-\d.*\.tgz$/ + 'npm:@sentry/wasm': + onlyIfPresent: /^sentry-wasm-\d.*\.tgz$/ diff --git a/.cursor/BUGBOT.md b/.cursor/BUGBOT.md new file mode 100644 index 000000000000..d70f36ff6c94 --- /dev/null +++ b/.cursor/BUGBOT.md @@ -0,0 +1,43 @@ +# PR Review Guidelines for Cursor Bot + +You are reviewing a pull request for the Sentry JavaScript SDK. +Flag any of the following indicators or missing requirements. +If you find anything to flag, mention that you flagged this in the review because it was mentioned in this rules file. +These issues are only relevant for production code. +Do not flag the issues below if they appear in tests. + +## Critical Issues to Flag + +### Security Vulnerabilities + +- Exposed secrets, API keys, tokens or creentials in code or comments +- Unsafe use of `eval()`, `Function()`, or `innerHTML` +- Unsafe regular expressions that could cause ReDoS attacks + +### Breaking Changes + +- Public API changes without proper deprecation notices +- Removal of publicly exported functions, classes, or types. Internal removals are fine! +- Changes to function signatures in public APIs + +## SDK-relevant issues + +### Performance Issues + +- Multiple loops over the same array (for example, using `.filter`, .`foreach`, chained). Suggest a classic `for` loop as a replacement. +- Memory leaks from event listeners, timers, or closures not being cleaned up or unsubscribing +- Large bundle size increases in browser packages. Sometimes they're unavoidable but flag them anyway. + +### Auto instrumentation, SDK integrations, Sentry-specific conventions + +- When calling any `startSpan` API (`startInactiveSpan`, `startSpanManual`, etc), always ensure that the following span attributes are set: + - `SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN` (`'sentry.origin'`) with a proper span origin + - `SEMANTIC_ATTRIBUTE_SENTRY_OP` (`'sentry.op'`) with a proper span op +- When calling `captureException`, always make sure that the `mechanism` is set: + - `handled`: must be set to `true` or `false` + - `type`: must be set to a proper origin (i.e. identify the integration and part in the integration that caught the exception). + - The type should align with the `SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN` if a span wraps the `captureException` call. + - If there's no direct span that's wrapping the captured exception, apply a proper `type` value, following the same naming + convention as the `SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN` value. +- When calling `startSpan`, check if error cases are handled. If flag that it might make sense to try/catch and call `captureException`. +- When calling `generateInstrumentationOnce`, the passed in name MUST match the name of the integration that uses it. If there are more than one instrumentations, they need to follow the pattern `${INSTRUMENTATION_NAME}.some-suffix`. diff --git a/.cursor/commands/publish_release.md b/.cursor/commands/publish_release.md new file mode 100644 index 000000000000..5d1477992dfb --- /dev/null +++ b/.cursor/commands/publish_release.md @@ -0,0 +1,5 @@ +# Release Command + +Execute the standard Sentry JavaScript SDK release process. + +Find the "publishing_release" rule in `.cursor/rules/publishing_release` and follow those complete instructions step by step. diff --git a/.cursor/environment.json b/.cursor/environment.json new file mode 100644 index 000000000000..0828f53fbd4a --- /dev/null +++ b/.cursor/environment.json @@ -0,0 +1,12 @@ +{ + "name": "Sentry JavaScript SDK Development", + "install": "curl https://get.volta.sh | bash && export VOLTA_HOME=\"$HOME/.volta\" && export PATH=\"$VOLTA_HOME/bin:$PATH\" && export VOLTA_FEATURE_PNPM=1 && yarn install", + "start": "export VOLTA_HOME=\"$HOME/.volta\" && export PATH=\"$VOLTA_HOME/bin:$PATH\" && export VOLTA_FEATURE_PNPM=1", + "terminals": [ + { + "name": "Development", + "command": "export VOLTA_HOME=\"$HOME/.volta\" && export PATH=\"$VOLTA_HOME/bin:$PATH\" && export VOLTA_FEATURE_PNPM=1 && echo 'Volta setup complete. Node version:' && node --version && echo 'Yarn version:' && yarn --version", + "description": "Main development terminal with Volta environment configured" + } + ] +} diff --git a/.cursor/rules/publishing_release.mdc b/.cursor/rules/publishing_release.mdc new file mode 100644 index 000000000000..4d6fecca5d2a --- /dev/null +++ b/.cursor/rules/publishing_release.mdc @@ -0,0 +1,33 @@ +--- +description: Use this rule if you are looking to publish a release for the Sentry JavaScript SDKs +globs: +alwaysApply: false +--- + +# Publishing a Release + +Use these guidelines when publishing a new Sentry JavaScript SDK release. + +## Standard Release Process (from develop to master) + +The release process is outlined in [publishing-a-release.md](mdc:docs/publishing-a-release.md). + +1. Make sure you are on the latest version of the `develop` branch. To confirm this, run `git pull origin develop` to get the latest changes from the repo. +2. Run `yarn changelog` on the `develop` branch and copy the output. You can use `yarn changelog | pbcopy` to copy the output of `yarn changelog` into your clipboard. +3. Decide on a version for the release based on [semver](mdc:https://semver.org). The version should be decided based on what is in included in the release. For example, if the release includes a new feature, we should increment the minor version. If it includes only bug fixes, we should increment the patch version. You can find the latest version in [CHANGELOG.md](mdc:CHANGELOG.md) at the very top. +4. Create a branch `prepare-release/VERSION`, eg. `prepare-release/8.1.0`, off `develop`. +5. Update [CHANGELOG.md](mdc:CHANGELOG.md) to add an entry for the next release number and a list of changes since the last release from the output of `yarn changelog`. See the `Updating the Changelog` section in [publishing-a-release.md](mdc:docs/publishing-a-release.md) for more details. If you remove changelog entries because they are not applicable, please let the user know. +6. Commit the changes to [CHANGELOG.md](mdc:CHANGELOG.md) with `meta(changelog): Update changelog for VERSION` where `VERSION` is the version of the release, e.g. `meta(changelog): Update changelog for 8.1.0` +7. Push the `prepare-release/VERSION` branch to origin and remind the user that the release PR needs to be opened from the `master` branch. + +## Key Commands + +- `yarn changelog` - Generate changelog entries +- `yarn lint` - Ensure code quality +- `yarn test` - Run test suite +- `yarn build:dev` - Verify build + +## Important Notes + +- This repository uses **Git Flow** - target `develop` for feature PRs, not `master`. See [gitflow.md](mdc:docs/gitflow.md) for more details. +- For first-time SDK releases, follow the New SDK Release Checklist [new-sdk-release-checklist.md](mdc:docs/new-sdk-release-checklist.md). If you notice there is something not following the new SDK release checklist, please remind the user. diff --git a/.cursor/rules/sdk_dependency_upgrades.mdc b/.cursor/rules/sdk_dependency_upgrades.mdc new file mode 100644 index 000000000000..becf0805eb91 --- /dev/null +++ b/.cursor/rules/sdk_dependency_upgrades.mdc @@ -0,0 +1,167 @@ +--- +description: Use this rule if you are looking to upgrade a dependency in the Sentry JavaScript SDKs +globs: +alwaysApply: false +--- + +# Yarn v1 Dependency Upgrades + +## Upgrade Process + +### Dependency Analysis + +```bash +# Check dependency tree +yarn list --depth=0 + +# Find why package is installed +yarn why [package-name] +``` + +### Root Workspace vs. Package Dependencies + +**CRITICAL**: Understand the difference between dependency types: + +- **Root Workspace dependencies**: Shared dev tools, build tools, testing frameworks +- **Package dependencies**: Package-specific runtime and dev dependencies +- Always check if dependency should be in root workspace or package level + +### Upgrade Dependencies + +**MANDATORY**: Only ever upgrade a single package at a time. + +**CRITICAL RULE**: If a dependency is not defined in `package.json` anywhere, the upgrade must be run in the root workspace as the `yarn.lock` file is contained there. + +```bash +# Upgrade specific package to latest compatible version +npx yarn-update-dependency@latest [package-name] +``` + +Avoid upgrading top-level dependencies (defined in `package.json`), especially if they are used for tests. If you are going to upgrade them, ask the user before proceeding. + +**REQUIREMENT**: If a `package.json` file is updated, make sure it has a new line at the end. + +#### CRITICAL: OpenTelemetry Dependency Constraint + +**STOP UPGRADE IMMEDIATELY** if upgrading any dependency with `opentelemetry` in the name and the new version or any of its dependencies uses forbidden OpenTelemetry versions. + +**FORBIDDEN VERSION PATTERNS:** + +- `2.x.x` versions (e.g., `2.0.0`, `2.1.0`) +- `0.2xx.x` versions (e.g., `0.200.0`, `0.201.0`) + +When upgrading OpenTelemetry dependencies: + +1. Check the dependency's `package.json` after upgrade +2. Verify the package itself doesn't use forbidden version patterns +3. Verify none of its dependencies use `@opentelemetry/*` packages with forbidden version patterns +4. **Example**: `@opentelemetry/instrumentation-pg@0.52.0` is forbidden because it bumped to core `2.0.0` and instrumentation `0.200.0` +5. If forbidden OpenTelemetry versions are detected, **ABORT the upgrade** and notify the user that this upgrade cannot proceed due to OpenTelemetry v2+ compatibility constraints + +#### CRITICAL: E2E Test Dependencies + +**DO NOT UPGRADE** the major version of dependencies in test applications where the test name explicitly mentions a dependency version. + +**RULE**: For `dev-packages/e2e-tests/test-applications/*`, if the test directory name mentions a specific version (e.g., `nestjs-8`), do not upgrade that dependency beyond the mentioned major version. + +**Example**: Do not upgrade the nestjs version of `dev-packages/e2e-tests/test-applications/nestjs-8` to nestjs 9 or above because the test name mentions nestjs 8. + +## Safety Protocols + +### Pre-Upgrade Checklist + +**COMPLETE ALL STEPS** before proceeding with any upgrade: + +1. **Backup**: Ensure clean git state or create backup branch +2. **CI Status**: Verify all tests are passing +3. **Lockfile works**: Confirm `yarn.lock` is in a good state (no merge conflicts) +4. **OpenTelemetry Check**: For OpenTelemetry dependencies, verify no forbidden version patterns (`2.x.x` or `0.2xx.x`) will be introduced + +### Post-Upgrade Verification + +```bash +# rebuild everything +yarn install + +# Build the project +yarn build:dev + +# Make sure dependencies are deduplicated +yarn dedupe-deps:fix + +# Fix any linting issues +yarn fix + +# Check circular dependencies +yarn circularDepCheck +``` + +## Version Management + +### Pinning Strategies + +- **Exact versions** (`1.2.3`): Use for critical dependencies +- **Caret versions** (`^1.2.3`): Allow minor updates only +- **Latest tag**: Avoid as much as possible other than in certain testing and development scenarios + +## Troubleshooting + +- **Yarn Version**: Run `yarn --version` - must be yarn v1 (not v2/v3/v4) +- **Lockfile Issues**: Verify yarn.lock exists and is properly maintained. Fix merge conflicts by running `yarn install` + +## Best Practices + +### Security Audits + +```bash +# Check for security vulnerabilities +yarn audit + +# Fix automatically fixable vulnerabilities +yarn audit fix + +# Install security patches only +yarn upgrade --security-only +``` + +### Check for Outdated Dependencies + +```bash +# Check all outdated dependencies +yarn outdated + +# Check specific package +yarn outdated [package-name] + +# Check dependencies in specific workspace +yarn workspace [workspace-name] outdated +``` + +### Using yarn info for Dependency Inspection + +Use `yarn info` to inspect package dependencies before and after upgrades: + +```bash +# Check current version and dependencies +yarn info + +# Check specific version dependencies +yarn info @ + +# Check dependencies field specifically +yarn info @ dependencies + +# Check all available versions +yarn info versions +``` + +The `yarn info` command provides detailed dependency information without requiring installation, making it particularly useful for: + +- Verifying OpenTelemetry packages don't introduce forbidden version patterns (`2.x.x` or `0.2xx.x`) +- Checking what dependencies a package will bring in before upgrading +- Understanding package version history and compatibility + +### Documentation + +- Update README or code comments if dependency change affects usage of the SDK or its integrations +- Notify team of significant changes diff --git a/.cursor/rules/sdk_development.mdc b/.cursor/rules/sdk_development.mdc new file mode 100644 index 000000000000..088c94f47a23 --- /dev/null +++ b/.cursor/rules/sdk_development.mdc @@ -0,0 +1,152 @@ +--- +description: Guidelines for working on the Sentry JavaScript SDK monorepo +alwaysApply: true +--- + +# SDK Development Rules + +You are working on the Sentry JavaScript SDK, a critical production SDK used by thousands of applications. Follow these rules strictly. + +## Code Quality Requirements (MANDATORY) + +**CRITICAL**: All changes must pass these checks before committing: + +1. **Always run `yarn lint`** - Fix all linting issues +2. **Always run `yarn test`** - Ensure all tests pass +3. **Always run `yarn build:dev`** - Verify TypeScript compilation + +## Development Commands + +### Build Commands + +- `yarn build` - Full production build with package verification +- `yarn build:dev` - Development build (transpile + types) +- `yarn build:dev:watch` - Development build in watch mode (recommended) +- `yarn build:dev:filter ` - Build specific package and dependencies +- `yarn build:types:watch` - Watch mode for TypeScript types only +- `yarn build:bundle` - Build browser bundles only + +### Testing + +- `yarn test` - Run all unit tests + +### Linting and Formatting + +- `yarn lint` - Run ESLint and Prettier checks +- `yarn fix` - Auto-fix linting and formatting issues +- `yarn lint:es-compatibility` - Check ES compatibility + +## Git Flow Branching Strategy + +This repository uses **Git Flow**. See [docs/gitflow.md](docs/gitflow.md) for details. + +### Key Rules + +- **All PRs target `develop` branch** (NOT `master`) +- `master` represents the last released state +- Never merge directly into `master` (except emergency fixes) +- Avoid changing `package.json` files on `develop` during pending releases +- Never update dependencies, package.json content or build scripts unless explicitly asked for +- When asked to do a task on a set of files, always make sure that all occurences in the codebase are covered. Double check that no files have been forgotten. +- Unless explicitly asked for, make sure to cover all files, including files in `src/` and `test/` directories. + +### Branch Naming + +- Features: `feat/descriptive-name` +- Releases: `release/X.Y.Z` + +## Repository Architecture + +This is a Lerna monorepo with 40+ packages in the `@sentry/*` namespace. + +### Core Packages + +- `packages/core/` - Base SDK with interfaces, type definitions, core functionality +- `packages/types/` - Shared TypeScript type definitions - this is deprecated, never modify this package +- `packages/browser-utils/` - Browser-specific utilities and instrumentation +- `packages/node-core/` - Node Core SDK which contains most of the node-specific logic, excluding OpenTelemetry instrumentation. + +### Platform SDKs + +- `packages/browser/` - Browser SDK with bundled variants +- `packages/node/` - Node.js SDK. All general Node code should go into node-core, the node package itself only contains OpenTelemetry instrumentation on top of that. +- `packages/bun/`, `packages/deno/`, `packages/cloudflare/` - Runtime-specific SDKs + +### Framework Integrations + +- Framework packages: `packages/{framework}/` (react, vue, angular, etc.) +- Client/server entry points where applicable (nextjs, nuxt, sveltekit) +- Integration tests use Playwright (Remix, browser-integration-tests) + +### User Experience Packages + +- `packages/replay-internal/` - Session replay functionality +- `packages/replay-canvas/` - Canvas recording for replay +- `packages/replay-worker/` - Web worker support for replay +- `packages/feedback/` - User feedback integration + +### Development Packages (`dev-packages/`) + +- `browser-integration-tests/` - Playwright browser tests +- `e2e-tests/` - End-to-end tests for 70+ framework combinations +- `node-integration-tests/` - Node.js integration tests +- `test-utils/` - Shared testing utilities +- `bundle-analyzer-scenarios/` - Bundle analysis +- `rollup-utils/` - Build utilities +- GitHub Actions packages for CI/CD automation + +## Development Guidelines + +### Build System + +- Uses Rollup for bundling (`rollup.*.config.mjs`) +- TypeScript with multiple tsconfig files per package +- Lerna manages package dependencies and publishing +- Vite for testing with `vitest` + +### Package Structure Pattern + +Each package typically contains: + +- `src/index.ts` - Main entry point +- `src/sdk.ts` - SDK initialization logic +- `rollup.npm.config.mjs` - Build configuration +- `tsconfig.json`, `tsconfig.test.json`, `tsconfig.types.json` +- `test/` directory with corresponding test files + +### Key Development Notes + +- Uses Volta for Node.js/Yarn version management +- Requires initial `yarn build` after `yarn install` for TypeScript linking +- Integration tests use Playwright extensively +- Never change the volta, yarn, or package manager setup in general unless explicitly asked for + +### Notes for Background Tasks + +- Make sure to use [volta](https://volta.sh/) for development. Volta is used to manage the node, yarn and pnpm version used. +- Make sure that [PNPM support is enabled in volta](https://docs.volta.sh/advanced/pnpm). This means that the `VOLTA_FEATURE_PNPM` environment variable has to be set to `1`. +- Yarn, Node and PNPM have to be used through volta, in the versions defined by the volta config. NEVER change any versions unless explicitly asked to. + +## Testing Single Packages + +- Test specific package: `cd packages/{package-name} && yarn test` +- Build specific package: `yarn build:dev:filter @sentry/{package-name}` + +## Code Style Rules + +- Follow existing code conventions in each package +- Check imports and dependencies - only use libraries already in the codebase +- Look at neighboring files for patterns and style +- Never introduce code that exposes secrets or keys +- Follow security best practices + +## Before Every Commit Checklist + +1. ✅ `yarn lint` (fix all issues) +2. ✅ `yarn test` (all tests pass) +3. ✅ `yarn build:dev` (builds successfully) +4. ✅ Target `develop` branch for PRs (not `master`) + +## Documentation Sync + +**IMPORTANT**: When editing CLAUDE.md, also update .cursor/rules/sdk_development.mdc and vice versa to keep both files in sync. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000000..20edaa21540a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 6d2ae3506864..000000000000 --- a/.eslintrc +++ /dev/null @@ -1,70 +0,0 @@ -{ - "env": { - "jasmine": true, - "node": true, - "mocha": true, - "browser": true, - "builtin": true - }, - "globals": { - "require": false - }, - "rules": { - "block-scoped-var": 2, - "dot-notation": [ - 2, - { - "allowKeywords": true - } - ], - "eqeqeq": [ - 2, - "allow-null" - ], - "guard-for-in": 2, - "new-cap": 2, - "no-caller": 2, - "no-cond-assign": [ - 2, - "except-parens" - ], - "no-debugger": 2, - "no-empty": 2, - "no-eval": 2, - "no-extend-native": 2, - "no-extra-parens": 2, - "no-irregular-whitespace": 2, - "no-iterator": 2, - "no-loop-func": 2, - "no-multi-str": 2, - "no-new": 2, - "no-proto": 2, - "no-script-url": 2, - "no-sequences": 2, - "no-shadow": 2, - "no-undef": 2, - "no-unused-vars": [ - 2, - {"args": "none"} - ], - "no-with": 2, - "quotes": [ - 2, - "single", - "avoid-escape" - ], - "semi": [ - 0, - "never" - ], - "strict": [ - 2, - "global" - ], - "valid-typeof": 2, - "wrap-iife": [ - 2, - "inside" - ] - } -} diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000000..5266f0117a89 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,70 @@ +// Note: All paths are relative to the directory in which eslint is being run, rather than the directory where this file +// lives + +// ESLint config docs: https://eslint.org/docs/user-guide/configuring/ + +module.exports = { + root: true, + env: { + es2017: true, + }, + parserOptions: { + ecmaVersion: 2020, + }, + extends: ['@sentry-internal/sdk'], + ignorePatterns: [ + 'coverage/**', + 'build/**', + 'dist/**', + 'cjs/**', + 'esm/**', + 'examples/**', + 'test/manual/**', + 'types/**', + 'scripts/*.js', + ], + reportUnusedDisableDirectives: true, + overrides: [ + { + files: ['*.ts', '*.tsx', '*.d.ts'], + parserOptions: { + project: ['tsconfig.json'], + }, + }, + { + files: ['test/**/*.ts', 'test/**/*.tsx'], + parserOptions: { + project: ['tsconfig.test.json'], + }, + }, + { + files: ['scripts/**/*.ts'], + parserOptions: { + project: ['tsconfig.dev.json'], + }, + }, + { + files: ['*.tsx'], + rules: { + // Turn off jsdoc on tsx files until jsdoc is fixed for tsx files + // See: https://github.com/getsentry/sentry-javascript/issues/3871 + 'jsdoc/require-jsdoc': 'off', + }, + }, + { + files: ['scenarios/**', 'dev-packages/rollup-utils/**', 'dev-packages/bundle-analyzer-scenarios/**'], + parserOptions: { + sourceType: 'module', + }, + rules: { + 'no-console': 'off', + }, + }, + { + files: ['vite.config.ts'], + parserOptions: { + project: ['tsconfig.test.json'], + }, + }, + ], +}; diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000000..6e908d5b21bb --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,26 @@ +# Since version 2.23 (released in August 2019), git-blame has a feature +# to ignore or bypass certain commits. +# +# This file contains a list of commits that are not likely what you +# are looking for in a blame, such as mass reformatting or renaming. + +# build: Add `@typescript-eslint/consistent-type-imports` rule (#6662) +2aa4e94b036675245290596884959e06dcced044 + +# chore: Rename `integration-tests` -> `browser-integration-tests` (#7455) +ef6b3c7877d5fc8031c08bb28b0ffafaeb01f501 + +# chore: Enforce formatting of MD files in repository root #10127 +aecf26f22dbf65ce2c0caadc4ce71b46266c9f45 + +# chore: Create dev-packages folder #9997 +35205b4cc5783237e69452c39ea001e461d9c84d + +# ref: Move node & node-experimental folders #11309 +# As well as revert and re-revert of this +971b51d4b8e92aa1b93c51074e28c7cbed63b486 +ebc9b539548953bb9dd81d6a18adcdd91e804563 +c88ff463a5566194a454b58bc555f183cf9ee813 + +# chore: Ensure prettier is run on all files #17497 +90edf65b3d93c89ae576b063a839541022f478cf diff --git a/.github/CANARY_FAILURE_TEMPLATE.md b/.github/CANARY_FAILURE_TEMPLATE.md new file mode 100644 index 000000000000..9e05fcfc44ae --- /dev/null +++ b/.github/CANARY_FAILURE_TEMPLATE.md @@ -0,0 +1,6 @@ +--- +title: '{{ env.TITLE }}' +labels: 'Type: Tests, Waiting for: Product Owner' +--- + +Canary tests failed: {{ env.RUN_LINK }} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000000..3bb7aa3860ff --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +packages/replay-internal @getsentry/replay-sdk-web +packages/replay-worker @getsentry/replay-sdk-web +packages/replay-canvas @getsentry/replay-sdk-web +packages/feedback @getsentry/feedback-sdk +dev-packages/browser-integration-tests/suites/replay @getsentry/replay-sdk-web diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 000000000000..8acac6fd2709 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,148 @@ +name: 🐞 Bug Report +description: Tell us about something that's not working the way we (probably) intend. +labels: ['Bug'] +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues + required: true + - label: I have reviewed the documentation https://docs.sentry.io/ + required: true + - label: I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases + required: true + - type: dropdown + id: type + attributes: + label: How do you use Sentry? + options: + - Sentry Saas (sentry.io) + - Self-hosted/on-premise + validations: + required: true + - type: dropdown + id: package + attributes: + label: Which SDK are you using? + description: + If you're using the CDN bundles, please specify the exact bundle (e.g. `bundle.tracing.min.js`) in your SDK + setup. + options: + - '@sentry/browser' + - '@sentry/node' + - '@sentry/node - express' + - '@sentry/node - fastify' + - '@sentry/node - koa' + - '@sentry/node - hapi' + - '@sentry/node - connect' + - '@sentry/node-native' + - '@sentry/angular' + - '@sentry/astro' + - '@sentry/aws-serverless' + - '@sentry/bun' + - '@sentry/cloudflare' + - '@sentry/cloudflare - hono' + - '@sentry/deno' + - '@sentry/ember' + - '@sentry/gatsby' + - '@sentry/google-cloud-serverless' + - '@sentry/nestjs' + - '@sentry/nextjs' + - '@sentry/nuxt' + - '@sentry/react' + - '@sentry/react-router' + - '@sentry/remix' + - '@sentry/solid' + - '@sentry/solidstart' + - '@sentry/svelte' + - '@sentry/sveltekit' + - '@sentry/tanstackstart-react' + - '@sentry/vue' + - '@sentry/wasm' + - Sentry Browser Loader + - Sentry Browser CDN bundle + validations: + required: true + - type: input + id: sdk-version + attributes: + label: SDK Version + description: What version of the SDK are you using? + placeholder: ex. 8.10.0 + validations: + required: true + - type: input + id: framework-version + attributes: + label: Framework Version + description: + If you're using one of our framework-specific SDKs (`@sentry/react`, for example), what version of the + _framework_ are you using? + placeholder: ex. React 18.3.0 or Next 14.0.0 + - type: input + id: link-to-sentry + attributes: + label: Link to Sentry event + description: + If applicable, please provide a link to the affected event from your Sentry account. The event will only be + viewable by Sentry staff; however, the event URL will still appear on your public GitHub issue. + placeholder: https://sentry.io/organizations//issues//events//?project= + - type: textarea + id: sdk-setup + attributes: + label: Reproduction Example/SDK Setup + description: + To ensure that we can help you as fast as possible, please share a link to a reproduction example (GitHub repo + or online code editor). This enables us to quickly understand and address your issue. If you do not post a link, + kindly paste your `Sentry.init` code, so we can see how you set up Sentry. + placeholder: |- + https://some-JS-online-code-editor.com/my-example + + ```javascript + Sentry.init({ + dsn: __YOUR_DSN__ + ... + }); + ``` + validations: + required: false + - type: textarea + id: repro + attributes: + label: Steps to Reproduce + description: How can we see what you're seeing? Specific is terrific. + placeholder: |- + 1. What + 2. you + 3. did. + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected Result + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual Result + description: Logs? Screenshots? Yes, please. + validations: + required: true + - type: textarea + id: additional + attributes: + label: Additional Context + description: + Add any other context here. Please keep the pre-filled text, which helps us manage issue prioritization. + value: |- + Tip: React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding `+1` or `me too`, to help us triage it. + validations: + required: false + - type: markdown + attributes: + value: |- + ## Thanks 🙏 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000000..8021a793fd49 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/getsentry/sentry-javascript/discussions + about: Ask questions and discuss with other community members diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml new file mode 100644 index 000000000000..2859c10d2dc0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -0,0 +1,40 @@ +name: 💡 Feature Request +description: Create a feature request for a sentry-javascript SDK. +labels: ['Feature'] +body: + - type: markdown + attributes: + value: Thanks for taking the time to file a feature request! Please fill out this form as completely as possible. + - type: textarea + id: problem + attributes: + label: Problem Statement + description: A clear and concise description of what you want and what your use case is. + placeholder: |- + I want to make whirled peas, but Sentry doesn't blend. + validations: + required: true + - type: textarea + id: expected + attributes: + label: Solution Brainstorm + description: We know you have bright ideas to share ... share away, friend. + placeholder: |- + Add a blender to Sentry. + validations: + required: true + - type: textarea + id: additional + attributes: + label: Additional Context + description: + Add any other context here. Please keep the pre-filled text, which helps us manage feature prioritization. + value: |- + Tip: React with 👍 to help prioritize this improvement. Please use comments to provide useful context, avoiding `+1` or `me too`, to help us triage it. + validations: + required: false + - type: markdown + attributes: + value: |- + ## Thanks 🙏 + Check our [triage docs](https://open.sentry.io/triage/) for what to expect next. diff --git a/.github/ISSUE_TEMPLATE/flaky.yml b/.github/ISSUE_TEMPLATE/flaky.yml new file mode 100644 index 000000000000..1b9290cc3bbc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/flaky.yml @@ -0,0 +1,45 @@ +name: ❅ Flaky Test +description: Report a flaky test in CI +title: '[Flaky CI]: ' +labels: ['Tests'] +body: + - type: dropdown + id: type + attributes: + label: Flakiness Type + description: What are you observing + options: + - Timeout + - Assertion failure + - Other / Unknown + validations: + required: true + - type: input + id: job-name + attributes: + label: Name of Job + placeholder: 'CI: Build & Test / Nextjs (Node 18) Tests' + description: name of job as reported in the status report + validations: + required: true + - type: input + id: test-name + attributes: + label: Name of Test + placeholder: suites/replay/captureReplay/test.ts + description: file name or function name of failing test + validations: + required: false + - type: input + id: test-run-link + attributes: + label: Link to Test Run + placeholder: https://github.com/getsentry/sentry/runs/5582673807 + description: paste the URL to a test run showing the issue + validations: + required: true + - type: textarea + id: details + attributes: + label: Details + description: If you know anything else, please add it here diff --git a/.github/ISSUE_TEMPLATE/internal.yml b/.github/ISSUE_TEMPLATE/internal.yml new file mode 100644 index 000000000000..bbd6b805ffb6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/internal.yml @@ -0,0 +1,12 @@ +name: 💡 [Internal] Blank Issue +description: Only for Sentry Employees! Create an issue without a template. +body: + - type: markdown + attributes: + value: Make sure to apply relevant labels and issue types before submitting. + - type: textarea + id: description + attributes: + label: Description + validations: + required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000000..9bc1ed1baab0 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,5 @@ +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: + +- [ ] If you've added code that should be tested, please add tests. +- [ ] Ensure your code lints and the test suite passes (`yarn lint`) & (`yarn test`). diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml new file mode 100644 index 000000000000..cfa664b1d219 --- /dev/null +++ b/.github/actions/install-dependencies/action.yml @@ -0,0 +1,27 @@ +name: 'Install yarn dependencies' +description: 'Installs yarn dependencies and caches them.' + +outputs: + cache_key: + description: 'The dependency cache key' + value: ${{ steps.compute_lockfile_hash.outputs.hash }} + +runs: + using: 'composite' + steps: + - name: Compute dependency cache key + id: compute_lockfile_hash + run: node ./scripts/dependency-hash-key.js >> "$GITHUB_OUTPUT" + shell: bash + + - name: Check dependency cache + uses: actions/cache@v4 + id: cache_dependencies + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ steps.compute_lockfile_hash.outputs.hash }} + + - name: Install dependencies + if: steps.cache_dependencies.outputs.cache-hit != 'true' + run: yarn install --ignore-engines --frozen-lockfile + shell: bash diff --git a/.github/actions/install-playwright/action.yml b/.github/actions/install-playwright/action.yml new file mode 100644 index 000000000000..8ca47ce04081 --- /dev/null +++ b/.github/actions/install-playwright/action.yml @@ -0,0 +1,52 @@ +name: 'Install Playwright dependencies' +description: 'Installs Playwright dependencies and caches them.' +inputs: + browsers: + description: 'What browsers to install.' + default: 'chromium webkit firefox' + cwd: + description: 'The working directory to run Playwright in.' + default: '.' + +runs: + using: 'composite' + steps: + - name: Get Playwright version + id: playwright-version + run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> $GITHUB_OUTPUT + shell: bash + working-directory: ${{ inputs.cwd }} + + - name: Restore cached playwright binaries + uses: actions/cache/restore@v4 + id: playwright-cache + with: + path: | + ~/.cache/ms-playwright + # Bump the iteration when bumping runner images to use a new cache + key: playwright-${{ runner.os }}-iteration-1-${{ 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 + if: steps.playwright-cache.outputs.cache-hit != 'true' + shell: bash + working-directory: ${{ inputs.cwd }} + + - name: Install Playwright system dependencies only (cached) + env: + PLAYWRIGHT_BROWSERS: ${{ inputs.browsers || 'chromium webkit firefox' }} + run: npx playwright install-deps "$PLAYWRIGHT_BROWSERS" + if: steps.playwright-cache.outputs.cache-hit == 'true' + shell: bash + working-directory: ${{ inputs.cwd }} + + # 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 + # Bump the iteration when bumping runner images to use a new cache + key: playwright-${{ runner.os }}-iteration-1-${{ steps.playwright-version.outputs.version }} diff --git a/.github/actions/restore-cache/action.yml b/.github/actions/restore-cache/action.yml new file mode 100644 index 000000000000..7e7a3971cd7e --- /dev/null +++ b/.github/actions/restore-cache/action.yml @@ -0,0 +1,27 @@ +name: 'Restore dependency & build cache' +description: 'Restore the dependency & build cache.' + +inputs: + dependency_cache_key: + description: 'The dependency cache key' + required: true + +runs: + using: 'composite' + steps: + - name: Check dependency cache + id: dep-cache + uses: actions/cache/restore@v4 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ inputs.dependency_cache_key }} + + - name: Restore build artifacts + uses: actions/download-artifact@v4 + with: + name: build-output + + - name: Install dependencies + if: steps.dep-cache.outputs.cache-hit != 'true' + run: yarn install --ignore-engines --frozen-lockfile + shell: bash diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 000000000000..d3ccaa70d705 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,4 @@ +paths-ignore: + # Our tsconfig files contain comments, which CodeQL complains about + - '**/tsconfig.json' + - '**/tsconfig.*.json' diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..1df50881932d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,26 @@ +version: 2 +updates: + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'monthly' + commit-message: + prefix: ci + prefix-development: ci + include: scope + - package-ecosystem: 'npm' + directory: '/' + schedule: + interval: 'weekly' + allow: + - dependency-name: '@sentry/*' + - dependency-name: '@opentelemetry/*' + - dependency-name: '@prisma/instrumentation' + - dependency-name: '@playwright/test' + versioning-strategy: increase + commit-message: + prefix: feat + prefix-development: feat + include: scope + exclude-paths: + - 'dev-packages/e2e-tests/test-applications/' diff --git a/.github/dependency-review-config.yml b/.github/dependency-review-config.yml new file mode 100644 index 000000000000..1a8f76e430d1 --- /dev/null +++ b/.github/dependency-review-config.yml @@ -0,0 +1,11 @@ +fail-on-severity: 'high' +allow-ghsas: + # dependency review does not allow specific file exclusions + # we use an older version of NextJS in our tests and thus need to + # exclude this + # once our minimum supported version is over 14.1.1 this can be removed + - GHSA-fr5h-rqp8-mj6g + # we need this for an E2E test for the minimum required version of Nuxt 3.7.0 + - GHSA-v784-fjjh-f8r4 + # Next.js Cache poisoning - We require a vulnerable version for E2E testing + - GHSA-gp8f-8m3g-qvj9 diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml new file mode 100644 index 000000000000..0507fe879c27 --- /dev/null +++ b/.github/workflows/auto-release.yml @@ -0,0 +1,60 @@ +name: 'Gitflow: Auto prepare release' +on: + pull_request: + types: + - closed + branches: + - master + +# This workflow tirggers a release when merging a branch with the pattern `prepare-release/VERSION` into master. +jobs: + release: + runs-on: ubuntu-24.04 + name: 'Prepare a new version' + + steps: + - name: Get auth token + id: token + uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 + with: + app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }} + private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }} + + - uses: actions/checkout@v5 + with: + token: ${{ steps.token.outputs.token }} + fetch-depth: 0 + + # https://github.com/actions-ecosystem/action-regex-match + - uses: actions-ecosystem/action-regex-match@v2 + id: version-regex + with: + # Parse version from head branch + text: ${{ github.head_ref }} + # match: preprare-release/xx.xx.xx + regex: '^prepare-release\/(\d+\.\d+\.\d+)(?:-(alpha|beta|rc)\.\d+)?$' + + - name: Extract version + id: get_version + run: | + version=${{ steps.version-regex.outputs.match }} + version=${version/'prepare-release/'/''} + echo "version=$version" >> $GITHUB_OUTPUT + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + + - name: Prepare release + uses: getsentry/action-prepare-release@v1 + if: + github.event.pull_request.merged == true && steps.version-regex.outputs.match != '' && + steps.get_version.outputs.version != '' + env: + GITHUB_TOKEN: ${{ steps.token.outputs.token }} + with: + version: ${{ steps.get_version.outputs.version }} + force: false + merge_target: master + craft_config_from_merge_target: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000000..4066a18eefe2 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,1179 @@ +name: 'CI: Build & Test' +on: + push: + branches: + - develop + - master + - v9 + - v8 + - release/** + pull_request: + merge_group: + types: [checks_requested] + workflow_dispatch: + inputs: + commit: + description: If the commit you want to test isn't the head of a branch, provide its SHA here + required: false + schedule: + # Run every day at midnight (without cache) + - cron: '0 0 * * *' + +# Cancel in progress workflows on pull_requests. +# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + HEAD_COMMIT: ${{ github.event.inputs.commit || github.sha }} + + # WARNING: this disables cross os caching as ~ and + # github.workspace evaluate to differents paths + CACHED_DEPENDENCY_PATHS: | + ${{ github.workspace }}/node_modules + ${{ github.workspace }}/packages/*/node_modules + ${{ github.workspace }}/dev-packages/*/node_modules + ~/.cache/mongodb-binaries/ + + # DEPENDENCY_CACHE_KEY: can't be set here because we don't have access to yarn.lock + + # WARNING: this disables cross os caching as ~ and + # github.workspace evaluate to differents paths + # packages/utils/cjs and packages/utils/esm: Symlinks to the folders inside of `build`, needed for tests + CACHED_BUILD_PATHS: | + ${{ github.workspace }}/dev-packages/*/build + ${{ github.workspace }}/packages/*/build + ${{ github.workspace }}/packages/*/lib + ${{ github.workspace }}/packages/ember/*.d.ts + ${{ github.workspace }}/packages/gatsby/*.d.ts + + BUILD_CACHE_TARBALL_KEY: tarball-${{ github.event.inputs.commit || github.sha }} + + # GH will use the first restore-key it finds that matches + # So it will start by looking for one from the same branch, else take the newest one it can find elsewhere + # We want to prefer the cache from the current develop branch, if we don't find any on the current branch + NX_CACHE_RESTORE_KEYS: | + nx-Linux-${{ github.ref }}-${{ github.event.inputs.commit || github.sha }} + nx-Linux-${{ github.ref }} + nx-Linux + + # https://bsky.app/profile/joyeecheung.bsky.social/post/3lhy6o54fo22h + # Apparently some of our CI failures are attributable to a corrupt v8 cache, causing v8 failures with: "Check failed: current == end_slot_index.". + # This option both controls the `v8-compile-cache-lib` and `v8-compile-cache` packages. + DISABLE_V8_COMPILE_CACHE: '1' + +jobs: + job_get_metadata: + name: Get Metadata + runs-on: ubuntu-24.04 + permissions: + pull-requests: read + steps: + - name: Check out current commit + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + # We need to check out not only the fake merge commit between the PR and the base branch which GH creates, but + # also its parents, so that we can pull the commit message from the head commit of the PR + fetch-depth: 2 + + - name: Get metadata + id: get_metadata + # We need to try a number of different options for finding the head commit, because each kind of trigger event + # stores it in a different location + run: | + COMMIT_SHA=$(git rev-parse --short ${{ github.event.pull_request.head.sha || github.event.head_commit.id || env.HEAD_COMMIT }}) + echo "COMMIT_SHA=$COMMIT_SHA" >> $GITHUB_ENV + echo "COMMIT_MESSAGE=$(git log -n 1 --pretty=format:%s $COMMIT_SHA)" >> $GITHUB_ENV + + # Most changed packages are determined in job_build via Nx + - name: Determine changed packages + uses: dorny/paths-filter@v3.0.1 + id: changed + with: + filters: | + workflow: + - '.github/**' + any_code: + - '!**/*.md' + + - name: Get PR labels + id: pr-labels + uses: mydea/pr-labels-action@fn/bump-node20 + + outputs: + commit_label: '${{ env.COMMIT_SHA }}: ${{ env.COMMIT_MESSAGE }}' + # Note: These next three have to be checked as strings ('true'/'false')! + is_base_branch: + ${{ github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/v9' || github.ref == 'refs/heads/v8'}} + is_release: ${{ startsWith(github.ref, 'refs/heads/release/') }} + changed_ci: ${{ steps.changed.outputs.workflow == 'true' }} + changed_any_code: ${{ steps.changed.outputs.any_code == 'true' }} + + # When merging into master, or from master + is_gitflow_sync: ${{ github.head_ref == 'master' || github.ref == 'refs/heads/master' }} + has_gitflow_label: + ${{ github.event_name == 'pull_request' && contains(steps.pr-labels.outputs.labels, ' Gitflow ') }} + force_skip_cache: + ${{ github.event_name == 'schedule' || (github.event_name == 'pull_request' && + contains(steps.pr-labels.outputs.labels, ' ci-skip-cache ')) }} + + job_build: + name: Build + needs: job_get_metadata + runs-on: ubuntu-24.04 + timeout-minutes: 15 + if: | + needs.job_get_metadata.outputs.changed_any_code == 'true' || + needs.job_get_metadata.outputs.is_base_branch == 'true' || + needs.job_get_metadata.outputs.is_release == 'true' || + (needs.job_get_metadata.outputs.is_gitflow_sync == 'false' && needs.job_get_metadata.outputs.has_gitflow_label == 'false') + steps: + - name: Check out base commit (${{ github.event.pull_request.base.sha }}) + uses: actions/checkout@v5 + if: github.event_name == 'pull_request' + with: + ref: ${{ github.event.pull_request.base.sha }} + + - name: 'Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }})' + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + + - name: Install Dependencies + uses: ./.github/actions/install-dependencies + id: install_dependencies + + - name: Check for Affected Nx Projects + uses: dkhunt27/action-nx-affected-list@v6.1 + id: checkForAffected + if: github.event_name == 'pull_request' + with: + base: ${{ github.event.pull_request.base.sha }} + head: ${{ env.HEAD_COMMIT }} + + - name: NX cache + uses: actions/cache@v4 + # Disable cache when: + # - on release branches + # - when PR has `ci-skip-cache` label or on nightly builds + if: | + needs.job_get_metadata.outputs.is_release == 'false' && + needs.job_get_metadata.outputs.force_skip_cache == 'false' + with: + path: .nxcache + key: nx-Linux-${{ github.ref }}-${{ env.HEAD_COMMIT || github.sha }} + # On develop branch, we want to _store_ the cache (so it can be used by other branches), but never _restore_ from it + restore-keys: + ${{needs.job_get_metadata.outputs.is_base_branch == 'false' && env.NX_CACHE_RESTORE_KEYS || + 'nx-never-restore'}} + + - name: Build packages + # Set the CODECOV_TOKEN for Bundle Analysis + env: + 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: 4 + 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') }} + changed_remix: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry/remix') }} + changed_node: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry/node') }} + changed_node_overhead_action: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry-internal/node-overhead-gh-action') }} + changed_deno: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry/deno') }} + changed_bun: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry/bun') }} + changed_browser_integration: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry-internal/browser-integration-tests') }} + + job_check_branches: + name: Check PR branches + needs: job_get_metadata + runs-on: ubuntu-24.04 + if: github.event_name == 'pull_request' + permissions: + pull-requests: write + steps: + - name: PR is opened against master + uses: mshick/add-pr-comment@dd126dd8c253650d181ad9538d8b4fa218fc31e8 + if: ${{ github.base_ref == 'master' && !startsWith(github.head_ref, 'prepare-release/') }} + with: + message: | + ⚠️ This PR is opened against **master**. You probably want to open it against **develop**. + + job_size_check: + name: Size Check + needs: [job_get_metadata, job_build] + timeout-minutes: 15 + runs-on: ubuntu-24.04 + if: + github.event_name == 'pull_request' || needs.job_get_metadata.outputs.is_base_branch == 'true' || + needs.job_get_metadata.outputs.is_release == 'true' + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Check bundle sizes + uses: ./dev-packages/size-limit-gh-action + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + # Only run comparison against develop if this is a PR + comparison_branch: ${{ (github.event_name == 'pull_request' && github.base_ref) || ''}} + + job_node_overhead_check: + name: Node Overhead Check + needs: [job_get_metadata, job_build] + timeout-minutes: 15 + runs-on: ubuntu-24.04 + if: + (needs.job_build.outputs.changed_node == 'true' && github.event_name == 'pull_request') || + (needs.job_build.outputs.changed_node_overhead_action == 'true' && github.event_name == 'pull_request') || + needs.job_get_metadata.outputs.is_base_branch == 'true' || needs.job_get_metadata.outputs.is_release == 'true' + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Check node overhead + uses: ./dev-packages/node-overhead-gh-action + env: + DEBUG: '1' + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + # Only run comparison against develop if this is a PR + comparison_branch: ${{ (github.event_name == 'pull_request' && github.base_ref) || ''}} + + job_lint: + name: Lint + # Even though the linter only checks source code, not built code, it needs the built code in order check that all + # inter-package dependencies resolve cleanly. + needs: [job_get_metadata, job_build] + timeout-minutes: 10 + runs-on: ubuntu-24.04 + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Check for duplicate dependencies in lockfile + # Run `yarn dedupe-deps:fix` locally to resolve any duplicates. + run: yarn dedupe-deps:check + - name: Lint source files + run: yarn lint:lerna + - name: Lint for ES compatibility + run: yarn lint:es-compatibility + + - name: Check that yarn.lock is stable + run: yarn && git diff --exit-code yarn.lock + + job_check_format: + name: Check file formatting + needs: [job_get_metadata] + timeout-minutes: 10 + runs-on: ubuntu-24.04 + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + + - name: Install Dependencies + uses: ./.github/actions/install-dependencies + id: install_dependencies + + - name: Check file formatting + run: yarn lint:prettier + + job_circular_dep_check: + name: Circular Dependency Check + needs: [job_get_metadata, job_build] + timeout-minutes: 10 + runs-on: ubuntu-24.04 + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Run madge + run: yarn circularDepCheck + + job_artifacts: + name: Upload Artifacts + needs: [job_get_metadata, job_build] + runs-on: ubuntu-24.04 + # Build artifacts are only needed for releasing workflow. + if: needs.job_get_metadata.outputs.is_release == 'true' + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + + - name: Pack tarballs + run: yarn build:tarball + + - name: Archive artifacts + 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/** + ${{ github.workspace }}/packages/replay-canvas/build/bundles/** + ${{ github.workspace }}/packages/feedback/build/bundles/** + ${{ github.workspace }}/packages/**/*.tgz + ${{ github.workspace }}/packages/aws-serverless/build/aws/dist-serverless/*.zip + + job_browser_unit_tests: + name: Browser Unit Tests + needs: [job_get_metadata, job_build] + timeout-minutes: 10 + runs-on: ubuntu-24.04 + steps: + - name: Check out base commit (${{ github.event.pull_request.base.sha }}) + uses: actions/checkout@v5 + if: github.event_name == 'pull_request' + with: + ref: ${{ github.event.pull_request.base.sha }} + + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + 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 }} + if: github.event_name == 'pull_request' + + - name: Run all tests + run: yarn test:ci:browser + if: github.event_name != 'pull_request' + + - name: Compute test coverage + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Upload test results to Codecov + if: cancelled() == false + continue-on-error: true + uses: codecov/test-results-action@v1 + with: + files: packages/**/*.junit.xml + token: ${{ secrets.CODECOV_TOKEN }} + + job_bun_unit_tests: + name: Bun Unit Tests + needs: [job_get_metadata, job_build] + if: needs.job_build.outputs.changed_bun == 'true' || github.event_name != 'pull_request' + timeout-minutes: 10 + runs-on: ubuntu-24.04 + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Set up Bun + uses: oven-sh/setup-bun@v2 + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Run tests + run: | + yarn test:ci:bun + + job_deno_unit_tests: + name: Deno Unit Tests + needs: [job_get_metadata, job_build] + if: needs.job_build.outputs.changed_deno == 'true' || github.event_name != 'pull_request' + timeout-minutes: 10 + runs-on: ubuntu-24.04 + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Set up Deno + uses: denoland/setup-deno@v2.0.3 + with: + deno-version: v2.1.5 + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Run tests + run: | + cd packages/deno + yarn build + yarn test + + job_node_unit_tests: + name: Node (${{ matrix.node }}) Unit Tests + needs: [job_get_metadata, job_build] + timeout-minutes: 10 + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + node: [18, 20, 22, 24] + steps: + - name: Check out base commit (${{ github.event.pull_request.base.sha }}) + uses: actions/checkout@v5 + if: github.event_name == 'pull_request' + with: + ref: ${{ github.event.pull_request.base.sha }} + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + + - name: Run affected tests + run: yarn test:pr:node --base=${{ github.event.pull_request.base.sha }} + if: github.event_name == 'pull_request' + env: + NODE_VERSION: ${{ matrix.node }} + + - name: Run all tests + run: yarn test:ci:node + if: github.event_name != 'pull_request' + env: + NODE_VERSION: ${{ matrix.node }} + + - name: Compute test coverage + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Upload test results to Codecov + if: cancelled() == false + continue-on-error: true + uses: codecov/test-results-action@v1 + with: + files: packages/**/*.junit.xml + token: ${{ secrets.CODECOV_TOKEN }} + + job_browser_playwright_tests: + name: + Playwright ${{ matrix.bundle }}${{ matrix.project && matrix.project != 'chromium' && format(' {0}', + matrix.project) || ''}}${{ matrix.shard && format(' ({0}/{1})', matrix.shard, matrix.shards) || ''}} Tests + needs: [job_get_metadata, job_build] + if: needs.job_build.outputs.changed_browser_integration == 'true' || github.event_name != 'pull_request' + runs-on: ubuntu-24.04-large-js + timeout-minutes: 25 + strategy: + fail-fast: false + matrix: + bundle: + - esm + - bundle + - bundle_min + - bundle_replay + - bundle_tracing + - bundle_tracing_replay + - bundle_tracing_replay_feedback + - bundle_tracing_replay_feedback_min + project: + - chromium + include: + # Only check all projects for full bundle + # We also shard the tests as they take the longest + - bundle: bundle_tracing_replay_feedback_min + project: 'webkit' + - bundle: bundle_tracing_replay_feedback_min + project: 'firefox' + - bundle: esm + project: chromium + shard: 1 + shards: 4 + - bundle: esm + project: chromium + shard: 2 + shards: 4 + - bundle: esm + project: chromium + shard: 3 + shards: 4 + - bundle: esm + project: chromium + shard: 4 + shards: 4 + exclude: + # Do not run the un-sharded esm tests + - bundle: esm + project: 'chromium' + + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + + - name: Install Playwright + uses: ./.github/actions/install-playwright + with: + browsers: ${{ matrix.project }} + + - name: Run Playwright tests + env: + PW_BUNDLE: ${{ matrix.bundle }} + working-directory: dev-packages/browser-integration-tests + run: + yarn test:all${{ 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@v4 + if: failure() + with: + 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 + + - name: Upload test results to Codecov + if: cancelled() == false + continue-on-error: true + uses: codecov/test-results-action@v1 + with: + directory: dev-packages/browser-integration-tests + token: ${{ secrets.CODECOV_TOKEN }} + + job_browser_loader_tests: + name: PW ${{ matrix.bundle }} Tests + needs: [job_get_metadata, job_build] + if: needs.job_build.outputs.changed_browser_integration == 'true' || github.event_name != 'pull_request' + runs-on: ubuntu-24.04 + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + bundle: + - loader_base + - loader_eager + - loader_debug + - loader_tracing + - loader_replay + - loader_replay_buffer + - loader_tracing_replay + + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + + - name: Install Playwright + uses: ./.github/actions/install-playwright + with: + browsers: chromium + + - name: Run Playwright Loader tests + env: + PW_BUNDLE: ${{ matrix.bundle }} + run: | + cd dev-packages/browser-integration-tests + yarn test:loader + + - name: Upload Playwright Traces + uses: actions/upload-artifact@v4 + if: failure() + with: + name: playwright-traces-job_browser_loader_tests-${{ matrix.bundle}} + path: dev-packages/browser-integration-tests/test-results + overwrite: true + retention-days: 7 + + - name: Upload test results to Codecov + if: cancelled() == false + continue-on-error: true + uses: codecov/test-results-action@v1 + with: + directory: dev-packages/browser-integration-tests + token: ${{ secrets.CODECOV_TOKEN }} + + job_check_for_faulty_dts: + name: Check for faulty .d.ts files + needs: [job_get_metadata, job_build] + runs-on: ubuntu-24.04 + timeout-minutes: 5 + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + 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 + echo "Found illegal TypeScript import statement." + exit 1 + fi + + job_node_integration_tests: + name: + Node (${{ matrix.node }})${{ (matrix.typescript && format(' (TS {0})', matrix.typescript)) || '' }} Integration + Tests + needs: [job_get_metadata, job_build] + if: needs.job_build.outputs.changed_node_integration == 'true' || github.event_name != 'pull_request' + runs-on: ubuntu-24.04 + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + node: [18, 20, 22, 24] + typescript: + - false + include: + # Only check typescript for latest version (to streamline CI) + - node: 24 + typescript: '3.8' + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + + - name: Overwrite typescript version + if: matrix.typescript == '3.8' + run: node ./scripts/use-ts-3_8.js + working-directory: dev-packages/node-integration-tests + + - name: Run integration tests + working-directory: dev-packages/node-integration-tests + run: yarn test + + - name: Upload test results to Codecov + if: cancelled() == false + continue-on-error: true + uses: codecov/test-results-action@v1 + with: + directory: dev-packages/node-integration-tests + token: ${{ secrets.CODECOV_TOKEN }} + + job_cloudflare_integration_tests: + name: Cloudflare Integration Tests + needs: [job_get_metadata, job_build] + runs-on: ubuntu-24.04 + timeout-minutes: 15 + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + + - name: Run integration tests + working-directory: dev-packages/cloudflare-integration-tests + run: yarn test + + job_remix_integration_tests: + name: Remix (Node ${{ matrix.node }}) Tests + needs: [job_get_metadata, job_build] + if: needs.job_build.outputs.changed_remix == 'true' || github.event_name != 'pull_request' + runs-on: ubuntu-24.04 + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + node: [18, 20, 22, 24] + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + + - name: Install Playwright + uses: ./.github/actions/install-playwright + with: + browsers: chromium + + - name: Run integration tests + env: + NODE_VERSION: ${{ matrix.node }} + run: | + cd packages/remix + yarn test:integration:ci + + - name: Upload test results to Codecov + if: cancelled() == false + continue-on-error: true + uses: codecov/test-results-action@v1 + with: + directory: packages/remix + token: ${{ secrets.CODECOV_TOKEN }} + + job_e2e_prepare: + name: Prepare E2E tests + # We want to run this if: + # - The build job was successful, not skipped + if: | + always() && + needs.job_build.result == 'success' + needs: [job_get_metadata, job_build] + runs-on: ubuntu-24.04-large-js + timeout-minutes: 15 + outputs: + matrix: ${{ steps.matrix.outputs.matrix }} + matrix-optional: ${{ steps.matrix-optional.outputs.matrix }} + steps: + - name: Check out base commit (${{ github.event.pull_request.base.sha }}) + uses: actions/checkout@v5 + if: github.event_name == 'pull_request' + with: + ref: ${{ github.event.pull_request.base.sha }} + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: NX cache + uses: actions/cache/restore@v4 + with: + path: .nxcache + key: nx-Linux-${{ github.ref }}-${{ env.HEAD_COMMIT }} + # On develop branch, we want to _store_ the cache (so it can be used by other branches), but never _restore_ from it + restore-keys: ${{ env.NX_CACHE_RESTORE_KEYS }} + + - name: Build tarballs + run: yarn build:tarball + + - name: Stores tarballs in cache + uses: actions/cache/save@v4 + with: + path: ${{ github.workspace }}/packages/*/*.tgz + key: ${{ env.BUILD_CACHE_TARBALL_KEY }} + + - name: Determine which E2E test applications should be run + id: matrix + run: + yarn --silent ci:build-matrix --base=${{ (github.event_name == 'pull_request' && + github.event.pull_request.base.sha) || '' }} >> $GITHUB_OUTPUT + working-directory: dev-packages/e2e-tests + + - name: Determine which optional E2E test applications should be run + id: matrix-optional + run: + yarn --silent ci:build-matrix-optional --base=${{ (github.event_name == 'pull_request' && + github.event.pull_request.base.sha) || '' }} >> $GITHUB_OUTPUT + working-directory: dev-packages/e2e-tests + + job_e2e_tests: + name: E2E ${{ matrix.label || matrix.test-application }} Test + # We need to add the `always()` check here because the previous step has this as well :( + # See: https://github.com/actions/runner/issues/2205 + if: + always() && needs.job_e2e_prepare.result == 'success' && needs.job_e2e_prepare.outputs.matrix != '{"include":[]}' + needs: [job_get_metadata, job_build, job_e2e_prepare] + runs-on: ubuntu-24.04 + timeout-minutes: 15 + env: + # We just use a dummy DSN here, only send to the tunnel anyhow + E2E_TEST_DSN: 'https://username@domain/123' + # Needed because some apps expect a certain prefix + NEXT_PUBLIC_E2E_TEST_DSN: 'https://username@domain/123' + PUBLIC_E2E_TEST_DSN: 'https://username@domain/123' + REACT_APP_E2E_TEST_DSN: 'https://username@domain/123' + E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' + E2E_TEST_SENTRY_PROJECT: 'sentry-javascript-e2e-tests' + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.job_e2e_prepare.outputs.matrix) }} + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - uses: pnpm/action-setup@v4 + with: + version: 9.15.9 + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/package.json' + - name: Set up Bun + if: matrix.test-application == 'node-exports-test-app' + uses: oven-sh/setup-bun@v2 + - name: Set up AWS SAM + if: matrix.test-application == 'aws-serverless' + uses: aws-actions/setup-sam@v2 + with: + use-installer: true + token: ${{ secrets.GITHUB_TOKEN }} + - name: Restore caches + uses: ./.github/actions/restore-cache + 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 }} + + - name: Build tarballs if not cached + if: steps.restore-tarball-cache.outputs.cache-hit != 'true' + run: yarn build:tarball + + - name: Get node version + id: versions + run: | + echo "echo node=$(jq -r '.volta.node' dev-packages/e2e-tests/package.json)" >> $GITHUB_OUTPUT + + - name: Validate Verdaccio + run: yarn test:validate + working-directory: dev-packages/e2e-tests + + - name: Prepare Verdaccio + run: yarn test:prepare + working-directory: dev-packages/e2e-tests + env: + E2E_TEST_PUBLISH_SCRIPT_NODE_VERSION: ${{ steps.versions.outputs.node }} + + - name: Copy to temp + run: yarn ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp }}/test-application + working-directory: dev-packages/e2e-tests + + - name: Build E2E app + working-directory: ${{ runner.temp }}/test-application + timeout-minutes: 7 + run: ${{ matrix.build-command || 'pnpm test:build' }} + + - name: Install Playwright + uses: ./.github/actions/install-playwright + with: + browsers: chromium + cwd: ${{ runner.temp }}/test-application + + - name: Run E2E test + working-directory: ${{ runner.temp }}/test-application + timeout-minutes: 10 + run: ${{ matrix.assert-command || '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: ${{ runner.temp }}/test-application/test-results + overwrite: true + retention-days: 7 + + - name: Pre-process E2E Test Dumps + if: failure() + run: | + node ./scripts/normalize-e2e-test-dump-transaction-events.js + + - name: Upload E2E Test Event Dumps + uses: actions/upload-artifact@v4 + if: failure() + with: + name: E2E Test Dump (${{ matrix.label || matrix.test-application }}) + path: ${{ runner.temp }}/test-application/event-dumps + overwrite: true + retention-days: 7 + if-no-files-found: ignore + + - name: Upload test results to Codecov + if: cancelled() == false + continue-on-error: true + uses: codecov/test-results-action@v1 + with: + directory: dev-packages/e2e-tests + token: ${{ secrets.CODECOV_TOKEN }} + + # - We skip optional tests on release branches + job_optional_e2e_tests: + name: E2E ${{ matrix.label || matrix.test-application }} Test (optional) + # We only run E2E tests for non-fork PRs because the E2E tests require secrets to work and they can't be accessed from forks + # We need to add the `always()` check here because the previous step has this as well :( + # See: https://github.com/actions/runner/issues/2205 + if: + always() && needs.job_get_metadata.outputs.is_release != 'true' && needs.job_e2e_prepare.result == 'success' && + needs.job_e2e_prepare.outputs.matrix-optional != '{"include":[]}' && (github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' + needs: [job_get_metadata, job_build, job_e2e_prepare] + runs-on: ubuntu-24.04 + timeout-minutes: 15 + env: + E2E_TEST_AUTH_TOKEN: ${{ secrets.E2E_TEST_AUTH_TOKEN }} + E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + # Needed because some apps expect a certain prefix + NEXT_PUBLIC_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + PUBLIC_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + REACT_APP_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' + E2E_TEST_SENTRY_PROJECT: 'sentry-javascript-e2e-tests' + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.job_e2e_prepare.outputs.matrix-optional) }} + + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - uses: pnpm/action-setup@v4 + with: + version: 9.15.9 + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + 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 }} + + - name: Build tarballs if not cached + if: steps.restore-tarball-cache.outputs.cache-hit != 'true' + run: yarn build:tarball + + - name: Get node version + id: versions + run: | + echo "echo node=$(jq -r '.volta.node' dev-packages/e2e-tests/package.json)" >> $GITHUB_OUTPUT + + - name: Validate Verdaccio + run: yarn test:validate + working-directory: dev-packages/e2e-tests + + - name: Prepare Verdaccio + run: yarn test:prepare + working-directory: dev-packages/e2e-tests + env: + E2E_TEST_PUBLISH_SCRIPT_NODE_VERSION: ${{ steps.versions.outputs.node }} + + - name: Copy to temp + run: yarn ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp }}/test-application + working-directory: dev-packages/e2e-tests + + - name: Build E2E app + working-directory: ${{ runner.temp }}/test-application + timeout-minutes: 7 + run: ${{ matrix.build-command || 'pnpm test:build' }} + + - name: Install Playwright + uses: ./.github/actions/install-playwright + with: + browsers: chromium + cwd: ${{ runner.temp }}/test-application + + - name: Run E2E test + working-directory: ${{ runner.temp }}/test-application + timeout-minutes: 10 + run: ${{ matrix.assert-command || 'pnpm test:assert' }} + + - name: Pre-process E2E Test Dumps + if: failure() + run: | + node ./scripts/normalize-e2e-test-dump-transaction-events.js + + - name: Upload E2E Test Event Dumps + uses: actions/upload-artifact@v4 + if: failure() + with: + name: E2E Test Dump (${{ matrix.label || matrix.test-application }}) + path: ${{ runner.temp }}/test-application/event-dumps + overwrite: true + retention-days: 7 + if-no-files-found: ignore + + - name: Deploy Astro to Cloudflare + uses: cloudflare/wrangler-action@v3 + if: matrix.test-application == 'cloudflare-astro' + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy dist --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }} + workingDirectory: ${{ runner.temp }}/test-application + + job_required_jobs_passed: + name: All required jobs passed or were skipped + needs: + [ + job_build, + job_browser_unit_tests, + job_bun_unit_tests, + job_deno_unit_tests, + job_node_unit_tests, + job_node_integration_tests, + job_cloudflare_integration_tests, + job_browser_playwright_tests, + job_browser_loader_tests, + job_remix_integration_tests, + job_e2e_tests, + job_artifacts, + job_lint, + job_check_format, + job_circular_dep_check, + ] + # Always run this, even if a dependent job failed + if: always() + runs-on: ubuntu-24.04 + steps: + - name: Check for failures + if: cancelled() || contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') + run: | + echo "One of the dependent jobs have failed. You may need to re-run it." && exit 1 diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml new file mode 100644 index 000000000000..fbf476c369a4 --- /dev/null +++ b/.github/workflows/canary.yml @@ -0,0 +1,182 @@ +name: 'CI: Canary Tests' +on: + schedule: + # Run every day at midnight + - cron: '0 0 * * *' + workflow_dispatch: + inputs: + commit: + description: If the commit you want to test isn't the head of a branch, provide its SHA here + required: false + +env: + HEAD_COMMIT: ${{ github.event.inputs.commit || github.sha }} + + CACHED_BUILD_PATHS: | + ${{ github.workspace }}/packages/*/*.tgz + ${{ github.workspace }}/node_modules + ${{ github.workspace }}/packages/*/node_modules + ${{ github.workspace }}/dev-packages/*/node_modules + ${{ github.workspace }}/dev-packages/*/build + ${{ github.workspace }}/packages/*/build + +permissions: + contents: read + issues: write + +jobs: + job_e2e_prepare: + name: Prepare E2E Canary tests + runs-on: ubuntu-24.04 + timeout-minutes: 30 + steps: + - name: Check out current commit + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Check canary cache + uses: actions/cache@v4 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: canary-${{ env.HEAD_COMMIT }} + - name: Install dependencies + run: yarn install + - name: Build packages + run: yarn build + + - name: Build tarballs + run: yarn build:tarball + + job_e2e_tests: + name: E2E ${{ matrix.label }} Test + needs: [job_e2e_prepare] + runs-on: ubuntu-24.04 + timeout-minutes: 20 + env: + # We just use a dummy DSN here, only send to the tunnel anyhow + E2E_TEST_DSN: 'https://username@domain/123' + # Needed because some apps expect a certain prefix + NEXT_PUBLIC_E2E_TEST_DSN: 'https://username@domain/123' + PUBLIC_E2E_TEST_DSN: 'https://username@domain/123' + REACT_APP_E2E_TEST_DSN: 'https://username@domain/123' + E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' + E2E_TEST_SENTRY_PROJECT: 'sentry-javascript-e2e-tests' + strategy: + fail-fast: false + matrix: + include: + - test-application: 'angular-20' + build-command: 'test:build-canary' + label: 'angular-20 (next)' + - test-application: 'create-react-app' + build-command: 'test:build-canary' + label: 'create-react-app (canary)' + - test-application: 'nextjs-app-dir' + build-command: 'test:build-canary' + label: 'nextjs-app-dir (canary)' + - test-application: 'nextjs-app-dir' + build-command: 'test:build-latest' + label: 'nextjs-app-dir (latest)' + - test-application: 'nextjs-13' + build-command: 'test:build-latest' + label: 'nextjs-13 (latest)' + - test-application: 'nextjs-14' + build-command: 'test:build-canary' + label: 'nextjs-14 (canary)' + - test-application: 'nextjs-14' + build-command: 'test:build-latest' + label: 'nextjs-14 (latest)' + - test-application: 'nextjs-15' + build-command: 'test:build-canary' + label: 'nextjs-15 (canary)' + - test-application: 'nextjs-15' + build-command: 'test:build-latest' + label: 'nextjs-15 (latest)' + - test-application: 'nextjs-turbo' + build-command: 'test:build-canary' + label: 'nextjs-turbo (canary)' + - test-application: 'nextjs-turbo' + build-command: 'test:build-latest' + label: 'nextjs-turbo (latest)' + - test-application: 'react-create-hash-router' + build-command: 'test:build-canary' + label: 'react-create-hash-router (canary)' + - test-application: 'react-router-6' + build-command: 'test:build-canary' + label: 'react-router-6 (canary)' + - test-application: 'nuxt-3' + build-command: 'test:build-canary' + label: 'nuxt-3 (canary)' + - test-application: 'nuxt-4' + build-command: 'test:build-canary' + label: 'nuxt-4 (canary)' + + steps: + - name: Check out current commit + uses: actions/checkout@v5 + with: + ref: ${{ env.HEAD_COMMIT }} + - uses: pnpm/action-setup@v4 + with: + version: 9.15.9 + - name: Set up Node + if: matrix.test-application != 'angular-20' + uses: actions/setup-node@v4 + with: + node-version-file: 'dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/package.json' + + - name: Restore canary cache + uses: actions/cache/restore@v4 + with: + path: ${{ env.CACHED_BUILD_PATHS }} + key: canary-${{ env.HEAD_COMMIT }} + + - name: Get node version + id: versions + run: | + echo "echo node=$(jq -r '.volta.node' dev-packages/e2e-tests/package.json)" >> $GITHUB_OUTPUT + + - name: Validate Verdaccio + run: yarn test:validate + working-directory: dev-packages/e2e-tests + + - name: Prepare Verdaccio + run: yarn test:prepare + working-directory: dev-packages/e2e-tests + env: + E2E_TEST_PUBLISH_SCRIPT_NODE_VERSION: ${{ steps.versions.outputs.node }} + + - name: Copy to temp + run: yarn ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp }}/test-application + working-directory: dev-packages/e2e-tests + + - name: Build E2E app + working-directory: ${{ runner.temp }}/test-application + timeout-minutes: 7 + run: yarn ${{ matrix.build-command }} + + - name: Install Playwright + uses: ./.github/actions/install-playwright + with: + browsers: chromium + cwd: ${{ runner.temp }}/test-application + + - name: Run E2E test + working-directory: ${{ runner.temp }}/test-application + timeout-minutes: 15 + run: yarn test:assert + + - name: Create Issue + if: failure() && github.event_name == 'schedule' + uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RUN_LINK: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + TITLE: ${{ matrix.label }} Test Failed + with: + filename: .github/CANARY_FAILURE_TEMPLATE.md + update_existing: true diff --git a/.github/workflows/cleanup-pr-caches.yml b/.github/workflows/cleanup-pr-caches.yml new file mode 100644 index 000000000000..2c9bba513605 --- /dev/null +++ b/.github/workflows/cleanup-pr-caches.yml @@ -0,0 +1,38 @@ +name: 'Automation: Cleanup PR caches' +on: + pull_request: + types: + - closed + +jobs: + cleanup: + runs-on: ubuntu-latest + permissions: + # `actions:write` permission is required to delete caches + # See also: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#delete-a-github-actions-cache-for-a-repository-using-a-cache-id + actions: write + contents: read + steps: + - name: Check out code + uses: actions/checkout@v5 + + - name: Cleanup + run: | + gh extension install actions/gh-actions-cache + + REPO=${{ github.repository }} + BRANCH=refs/pull/${{ github.event.pull_request.number }}/merge + + echo "Fetching list of cache key" + cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) + + ## Setting this to not fail the workflow while deleting cache keys. + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR + do + gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm + done + echo "Done" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/clear-cache.yml b/.github/workflows/clear-cache.yml new file mode 100644 index 000000000000..97aeb53365e7 --- /dev/null +++ b/.github/workflows/clear-cache.yml @@ -0,0 +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-24.04 + steps: + - uses: actions/checkout@v5 + + - 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/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000000..8c042c5aa44f --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,80 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: 'CI: CodeQL' + +on: + push: + branches: [develop] + pull_request: + # The branches below must be a subset of the branches above + branches: [develop] + paths-ignore: + # When _only_ changing .md files, no need to run CodeQL analysis + - '**/*.md' + schedule: + - cron: '40 3 * * 0' + +# Cancel in progress workflows on pull_requests. +# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + # Skip for pushes from dependabot, which is not supported + if: github.event_name == 'pull_request' || github.actor != 'dependabot[bot]' + + strategy: + fail-fast: false + matrix: + language: ['javascript'] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + config-file: ./.github/codeql/codeql-config.yml + queries: security-extended + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/enforce-license-compliance.yml b/.github/workflows/enforce-license-compliance.yml new file mode 100644 index 000000000000..0f186ad9a7a0 --- /dev/null +++ b/.github/workflows/enforce-license-compliance.yml @@ -0,0 +1,25 @@ +name: 'CI: Enforce License Compliance' + +on: + push: + branches: + - develop + - master + - v9 + - v8 + - release/** + pull_request: + branches: + - develop + - master + - v9 + - v8 + +jobs: + enforce-license-compliance: + runs-on: ubuntu-24.04 + steps: + - name: 'Enforce License Compliance' + uses: getsentry/action-enforce-license-compliance@main + with: + fossa_api_key: ${{ secrets.FOSSA_API_KEY }} diff --git a/.github/workflows/external-contributors.yml b/.github/workflows/external-contributors.yml new file mode 100644 index 000000000000..1735a89a5446 --- /dev/null +++ b/.github/workflows/external-contributors.yml @@ -0,0 +1,51 @@ +name: 'CI: Mention external contributors' +on: + pull_request_target: + types: + - closed + branches: + - develop + +jobs: + external_contributor: + name: External Contributors + permissions: + pull-requests: write + contents: write + runs-on: ubuntu-24.04 + if: | + github.event.pull_request.merged == true + && github.event.pull_request.author_association != 'COLLABORATOR' + && github.event.pull_request.author_association != 'MEMBER' + && github.event.pull_request.author_association != 'OWNER' + && endsWith(github.event.pull_request.user.login, '[bot]') == false + steps: + - uses: actions/checkout@v5 + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Add external contributor to CHANGELOG.md + uses: ./dev-packages/external-contributor-gh-action + with: + name: ${{ github.event.pull_request.user.login }} + author_association: ${{ github.event.pull_request.author_association }} + + - name: Create PR with changes + uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e + with: + # This token is scoped to Daniel Griesser + # If we used the default GITHUB_TOKEN, the resulting PR would not trigger CI :( + token: ${{ secrets.REPO_SCOPED_TOKEN }} + commit-message: 'chore: Add external contributor to CHANGELOG.md' + title: 'chore: Add external contributor to CHANGELOG.md' + branch: 'external-contributor/patch-${{ github.event.pull_request.user.login }}' + base: 'develop' + delete-branch: true + body: + 'This PR adds the external contributor to the CHANGELOG.md file, so that they are credited for their + contribution. See #${{ github.event.pull_request.number }}' diff --git a/.github/workflows/flaky-test-detector.yml b/.github/workflows/flaky-test-detector.yml new file mode 100644 index 000000000000..5103f1f43a2d --- /dev/null +++ b/.github/workflows/flaky-test-detector.yml @@ -0,0 +1,79 @@ +name: 'CI: Detect flaky tests' +on: + workflow_dispatch: + pull_request: + paths: + - 'dev-packages/browser-integration-tests/suites/**/test.ts' + branches-ignore: + - master + +env: + HEAD_COMMIT: ${{ github.event.inputs.commit || github.sha }} + + NX_CACHE_RESTORE_KEYS: | + nx-Linux-${{ github.ref }}-${{ github.event.inputs.commit || github.sha }} + nx-Linux-${{ github.ref }} + nx-Linux + +# Cancel in progress workflows on pull_requests. +# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + flaky-detector: + runs-on: ubuntu-24.04 + timeout-minutes: 60 + name: 'Check tests for flakiness' + # Also skip if PR is from master -> develop + if: ${{ github.base_ref != 'master' && github.ref != 'refs/heads/master' }} + steps: + - name: Check out current branch + uses: actions/checkout@v5 + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + cache: 'yarn' + - name: Install dependencies + run: yarn install --ignore-engines --frozen-lockfile + + - name: NX cache + uses: actions/cache/restore@v4 + with: + path: .nxcache + key: nx-Linux-${{ github.ref }}-${{ env.HEAD_COMMIT }} + restore-keys: ${{ env.NX_CACHE_RESTORE_KEYS }} + + - name: Build packages + run: yarn build + + - name: Install Playwright + uses: ./.github/actions/install-playwright + with: + browsers: 'chromium' + + - name: Determine changed tests + uses: dorny/paths-filter@v3.0.1 + id: changed + with: + list-files: json + filters: | + browser_integration: dev-packages/browser-integration-tests/suites/**/test.ts + + - name: Detect flaky tests + id: test + run: yarn test:detect-flaky + working-directory: dev-packages/browser-integration-tests + env: + CHANGED_TEST_PATHS: ${{ steps.changed.outputs.browser_integration_files }} + TEST_RUN_COUNT: 'AUTO' + + - name: Upload Playwright Traces + uses: actions/upload-artifact@v4 + if: failure() && steps.test.outcome == 'failure' + with: + name: playwright-test-results + path: dev-packages/browser-integration-tests/test-results + retention-days: 5 diff --git a/.github/workflows/gitflow-sync-develop.yml b/.github/workflows/gitflow-sync-develop.yml new file mode 100644 index 000000000000..96c69d952264 --- /dev/null +++ b/.github/workflows/gitflow-sync-develop.yml @@ -0,0 +1,53 @@ +name: 'Gitflow: Sync master into develop' +on: + push: + branches: + - master + paths: + # When the version is updated on master (but nothing else) + - 'lerna.json' + - '!**/*.js' + - '!**/*.ts' + workflow_dispatch: + +env: + SOURCE_BRANCH: master + TARGET_BRANCH: develop + +jobs: + main: + name: Create PR master->develop + runs-on: ubuntu-24.04 + permissions: + pull-requests: write + contents: write + steps: + - name: git checkout + uses: actions/checkout@v5 + + # https://github.com/marketplace/actions/github-pull-request-action + - name: Create Pull Request + id: open-pr + uses: repo-sync/pull-request@v2 + with: + source_branch: ${{ env.SOURCE_BRANCH }} + destination_branch: ${{ env.TARGET_BRANCH }} + pr_title: '[Gitflow] Merge ${{ env.SOURCE_BRANCH }} into ${{ env.TARGET_BRANCH }}' + pr_body: 'Merge ${{ env.SOURCE_BRANCH }} branch into ${{ env.TARGET_BRANCH }}' + pr_label: 'Dev: Gitflow' + # This token is scoped to Daniel Griesser + github_token: ${{ secrets.REPO_SCOPED_TOKEN }} + + - name: Enable automerge for PR + if: steps.open-pr.outputs.pr_number != '' + run: gh pr merge --merge --auto "${{ steps.open-pr.outputs.pr_number }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # https://github.com/marketplace/actions/auto-approve + - name: Auto approve PR + if: steps.open-pr.outputs.pr_number != '' + uses: hmarr/auto-approve-action@v4 + with: + pull-request-number: ${{ steps.open-pr.outputs.pr_number }} + review-message: 'Auto approved automated PR' diff --git a/.github/workflows/issue-package-label.yml b/.github/workflows/issue-package-label.yml new file mode 100644 index 000000000000..ef0f0344b8fc --- /dev/null +++ b/.github/workflows/issue-package-label.yml @@ -0,0 +1,162 @@ +name: 'Automation: Tag issue with package label' + +on: + issues: + types: [opened] + +jobs: + add_labels: + name: Add package label + runs-on: ubuntu-latest + if: ${{ !github.event.issue.pull_request }} + steps: + - name: Get used package from issue body + # https://github.com/actions-ecosystem/action-regex-match + uses: actions-ecosystem/action-regex-match@v2 + id: packageName + with: + # Parse used package from issue body + text: ${{ github.event.issue.body }} + regex: '### Which SDK are you using\?\n\n(.*)\n\n' + + - name: Map package to issue label + # https://github.com/kanga333/variable-mapper + uses: kanga333/variable-mapper@v0.3.0 + id: packageLabel + if: steps.packageName.outputs.match != '' + with: + key: '${{ steps.packageName.outputs.group1 }}' + # Note: Since this is handled as a regex, and JSON parse wrangles slashes /, we just use `.` instead + map: | + { + "@sentry.angular": { + "label": "Angular" + }, + "@sentry.astro": { + "label": "Astro" + }, + "@sentry.aws-serverless": { + "label": "AWS Lambda" + }, + "@sentry.browser": { + "label": "Browser" + }, + "@sentry.bun": { + "label": "Bun" + }, + "@sentry.cloudflare.-.hono": { + "label": "Hono" + }, + "@sentry.cloudflare": { + "label": "Cloudflare Workers" + }, + "@sentry.deno": { + "label": "Deno" + }, + "@sentry.ember": { + "label": "Ember" + }, + "@sentry.gatsby": { + "label": "Gatbsy" + }, + "@sentry.google-cloud-serverless": { + "label": "Google Cloud Functions" + }, + "@sentry.nestjs": { + "label": "Nest.js" + }, + "@sentry.nextjs": { + "label": "Next.js" + }, + "@sentry.node.-.express": { + "label": "Express" + }, + "@sentry.node.-.fastify": { + "label": "Fastify" + }, + "@sentry.node.-.koa": { + "label": "Koa" + }, + "@sentry.node.-.hapi": { + "label": "Hapi" + }, + "@sentry.node.-.connect": { + "label": "Connect" + }, + "@sentry.node": { + "label": "Node.js" + }, + "@sentry.nuxt": { + "label": "Nuxt" + }, + "@sentry.react-router": { + "label": "React Router Framework" + }, + "@sentry.react": { + "label": "React" + }, + "@sentry.remix": { + "label": "Remix" + }, + "@sentry.solid": { + "label": "Solid" + }, + "@sentry.solidstart": { + "label": "SolidStart" + }, + "@sentry.sveltekit": { + "label": "SvelteKit" + }, + "@sentry.svelte": { + "label": "Svelte" + }, + "@sentry.vue": { + "label": "Vue" + }, + "@sentry.tanstackstart-react": { + "label": "Tanstack Start React" + }, + "@sentry.wasm": { + "label": "WASM" + }, + "Sentry.Browser.Loader": { + "label": "Browser" + }, + "Sentry.Browser.CDN.bundle": { + "label": "Browser" + } + } + export_to: output + + - name: Add package label if applicable + # Note: We only add the label if the issue is still open + if: steps.packageLabel.outputs.label != '' + uses: actions-ecosystem/action-add-labels@v1 + with: + labels: ${{ steps.packageLabel.outputs.label }} + + - name: Map additional to issue label + # https://github.com/kanga333/variable-mapper + uses: kanga333/variable-mapper@v0.3.0 + id: additionalLabel + if: steps.packageName.outputs.match != '' + with: + key: '${{ steps.packageName.outputs.group1 }}' + # Note: Since this is handled as a regex, and JSON parse wrangles slashes /, we just use `.` instead + map: | + { + "Sentry.Browser.Loader": { + "label": "Loader Script" + }, + "Sentry.Browser.CDN.bundle": { + "label": "CDN Bundle" + } + } + export_to: output + + - name: Add additional label if applicable + # Note: We only add the label if the issue is still open + if: steps.additionalLabel.outputs.label != '' + uses: actions-ecosystem/action-add-labels@v1 + with: + labels: ${{ steps.additionalLabel.outputs.label }} diff --git a/.github/workflows/release-comment-issues.yml b/.github/workflows/release-comment-issues.yml new file mode 100644 index 000000000000..dfb782b1b6d8 --- /dev/null +++ b/.github/workflows/release-comment-issues.yml @@ -0,0 +1,35 @@ +name: 'Automation: Notify issues for release' +on: + release: + types: + - published + workflow_dispatch: + inputs: + version: + description: Which version to notify issues for + required: false + +# This workflow is triggered when a release is published +jobs: + release-comment-issues: + runs-on: ubuntu-24.04 + name: 'Notify issues' + steps: + - name: Get version + id: get_version + env: + INPUTS_VERSION: ${{ github.event.inputs.version }} + RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} + run: echo "version=${INPUTS_VERSION:-$RELEASE_TAG_NAME}" >> "$GITHUB_OUTPUT" + + - name: Comment on linked issues that are mentioned in release + if: | + steps.get_version.outputs.version != '' + && !contains(steps.get_version.outputs.version, '-beta.') + && !contains(steps.get_version.outputs.version, '-alpha.') + && !contains(steps.get_version.outputs.version, '-rc.') + + uses: getsentry/release-comment-issues-gh-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + version: ${{ steps.get_version.outputs.version }} diff --git a/.github/workflows/release-size-info.yml b/.github/workflows/release-size-info.yml new file mode 100644 index 000000000000..a1f75303d1ff --- /dev/null +++ b/.github/workflows/release-size-info.yml @@ -0,0 +1,33 @@ +name: 'Automation: Add size info to release' +on: + release: + types: + - published + workflow_dispatch: + inputs: + version: + description: Which version to add size info for + required: false + +# This workflow is triggered when a release is published +# It fetches the size-limit info from the release branch and adds it to the release +jobs: + release-size-info: + runs-on: ubuntu-24.04 + name: 'Add size-limit info to release' + + steps: + - name: Get version + id: get_version + env: + INPUTS_VERSION: ${{ github.event.inputs.version }} + RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} + run: echo "version=${INPUTS_VERSION:-$RELEASE_TAG_NAME}" >> "$GITHUB_OUTPUT" + + - name: Update Github Release + if: steps.get_version.outputs.version != '' + uses: getsentry/size-limit-release@v2 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + version: ${{ steps.get_version.outputs.version }} + workflow_name: 'CI: Build & Test' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000000..05c465036ce4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,42 @@ +name: 'Action: Prepare Release' +on: + workflow_dispatch: + inputs: + version: + description: Version to release + required: true + force: + description: Force a release even when there are release-blockers (optional) + required: false + merge_target: + description: Target branch to merge into. Uses the default branch as a fallback (optional) + required: false + default: master +jobs: + release: + runs-on: ubuntu-24.04 + name: 'Release a new version' + steps: + - name: Get auth token + id: token + uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 + with: + app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }} + private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }} + - uses: actions/checkout@v5 + with: + token: ${{ steps.token.outputs.token }} + fetch-depth: 0 + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + - name: Prepare release + uses: getsentry/action-prepare-release@v1 + env: + GITHUB_TOKEN: ${{ steps.token.outputs.token }} + with: + version: ${{ github.event.inputs.version }} + force: ${{ github.event.inputs.force }} + merge_target: ${{ github.event.inputs.merge_target }} + craft_config_from_merge_target: true diff --git a/.gitignore b/.gitignore index 94bd7b842a83..f381e7e6e24d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,63 @@ -.DS_Store +# dependencies +node_modules/ +packages/*/package-lock.json +dev-packages/*/package-lock.json +package-lock.json -# Thumbnails -._* +# build and test +# SDK builds +build/ +# various integration test builds +dist/ +coverage/ +scratch/ +*.js.map +*.pyc +*.tsbuildinfo +# side effects of running AWS lambda layer zip action locally +dist-serverless/ +sentry-node-serverless-*.zip +# node tarballs +packages/*/sentry-*.tgz +.nxcache + +# logs +yarn-error.log +npm-debug.log +lerna-debug.log +local.log + +# ide +.idea +*.sublime-* -# Files that might appear on external disk +# misc +.DS_Store +._* .Spotlight-V100 .Trashes +.nx -docs/_build -docs/doctrees +.rpt2_cache -build -node_modules -npm-debug.log +lint-results.json +trace.zip -scratch/ +# legacy +tmp.js -*.pyc +# eslint +.eslintcache +**/eslintcache/* -.idea -aws.json +# deno +packages/deno/build-types +packages/deno/build-test +packages/deno/lib.deno.d.ts + +# gatsby +packages/gatsby/gatsby-node.d.ts + +# intellij +*.iml +/**/.wrangler/* diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 1e6464ab94bf..000000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "docs/_sentryext"] - path = docs/_sentryext - url = https://github.com/getsentry/sentry-doc-support diff --git a/.madgerc b/.madgerc new file mode 100644 index 000000000000..b407c6b4b14f --- /dev/null +++ b/.madgerc @@ -0,0 +1,7 @@ +{ + "detectiveOptions": { + "ts": { + "skipTypeImports": true + } + } +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000000..99c0d942024b --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +packages/browser/test/loader.js +packages/replay-worker/examples/worker.min.js +dev-packages/browser-integration-tests/fixtures +**/test.ts-snapshots/** diff --git a/.secret_scan_ignore b/.secret_scan_ignore new file mode 100644 index 000000000000..96a69577ba5d --- /dev/null +++ b/.secret_scan_ignore @@ -0,0 +1 @@ +packages\/google-cloud-serverless\/test\/integrations\/private\.pem diff --git a/.size-limit.js b/.size-limit.js new file mode 100644 index 000000000000..59ad29c3ccf8 --- /dev/null +++ b/.size-limit.js @@ -0,0 +1,276 @@ +const builtinModules = require('module').builtinModules; +const nodePrefixedBuiltinModules = builtinModules.map(m => `node:${m}`); + +module.exports = [ + // Browser SDK (ESM) + { + name: '@sentry/browser', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init'), + gzip: true, + limit: '25 KB', + }, + { + name: '@sentry/browser - with treeshaking flags', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init'), + gzip: true, + limit: '24.1 KB', + modifyWebpackConfig: function (config) { + const webpack = require('webpack'); + + 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; + + return config; + }, + }, + { + name: '@sentry/browser (incl. Tracing)', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init', 'browserTracingIntegration'), + gzip: true, + limit: '40.7 KB', + }, + { + name: '@sentry/browser (incl. Tracing, Replay)', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), + gzip: true, + limit: '80 KB', + }, + { + name: '@sentry/browser (incl. Tracing, Replay) - with treeshaking flags', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), + gzip: true, + limit: '75 KB', + modifyWebpackConfig: function (config) { + const webpack = require('webpack'); + + 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; + + return config; + }, + }, + { + name: '@sentry/browser (incl. Tracing, Replay with Canvas)', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'replayCanvasIntegration'), + gzip: true, + limit: '84 KB', + }, + { + name: '@sentry/browser (incl. Tracing, Replay, Feedback)', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init', 'browserTracingIntegration', 'replayIntegration', 'feedbackIntegration'), + gzip: true, + limit: '96 KB', + }, + { + name: '@sentry/browser (incl. Feedback)', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init', 'feedbackIntegration'), + gzip: true, + limit: '42 KB', + }, + { + name: '@sentry/browser (incl. sendFeedback)', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init', 'sendFeedback'), + gzip: true, + limit: '30 KB', + }, + { + name: '@sentry/browser (incl. FeedbackAsync)', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init', 'feedbackAsyncIntegration'), + gzip: true, + limit: '34 KB', + }, + // React SDK (ESM) + { + name: '@sentry/react', + path: 'packages/react/build/esm/index.js', + import: createImport('init', 'ErrorBoundary'), + ignore: ['react/jsx-runtime'], + gzip: true, + limit: '27 KB', + }, + { + name: '@sentry/react (incl. Tracing)', + path: 'packages/react/build/esm/index.js', + import: createImport('init', 'ErrorBoundary', 'reactRouterV6BrowserTracingIntegration'), + ignore: ['react/jsx-runtime'], + gzip: true, + limit: '43 KB', + }, + // Vue SDK (ESM) + { + name: '@sentry/vue', + path: 'packages/vue/build/esm/index.js', + import: createImport('init'), + gzip: true, + limit: '29 KB', + }, + { + name: '@sentry/vue (incl. Tracing)', + path: 'packages/vue/build/esm/index.js', + import: createImport('init', 'browserTracingIntegration'), + gzip: true, + limit: '43 KB', + }, + // Svelte SDK (ESM) + { + name: '@sentry/svelte', + path: 'packages/svelte/build/esm/index.js', + import: createImport('init'), + gzip: true, + limit: '25 KB', + }, + // Browser CDN bundles + { + name: 'CDN Bundle', + path: createCDNPath('bundle.min.js'), + gzip: true, + limit: '27 KB', + }, + { + name: 'CDN Bundle (incl. Tracing)', + path: createCDNPath('bundle.tracing.min.js'), + gzip: true, + limit: '42 KB', + }, + { + name: 'CDN Bundle (incl. Tracing, Replay)', + path: createCDNPath('bundle.tracing.replay.min.js'), + gzip: true, + limit: '80 KB', + }, + { + name: 'CDN Bundle (incl. Tracing, Replay, Feedback)', + path: createCDNPath('bundle.tracing.replay.feedback.min.js'), + gzip: true, + limit: '86 KB', + }, + // browser CDN bundles (non-gzipped) + { + name: 'CDN Bundle - uncompressed', + path: createCDNPath('bundle.min.js'), + gzip: false, + brotli: false, + limit: '80 KB', + }, + { + name: 'CDN Bundle (incl. Tracing) - uncompressed', + path: createCDNPath('bundle.tracing.min.js'), + gzip: false, + brotli: false, + limit: '123 KB', + }, + { + name: 'CDN Bundle (incl. Tracing, Replay) - uncompressed', + path: createCDNPath('bundle.tracing.replay.min.js'), + gzip: false, + brotli: false, + limit: '240 KB', + }, + { + name: 'CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed', + path: createCDNPath('bundle.tracing.replay.feedback.min.js'), + gzip: false, + brotli: false, + limit: '264 KB', + }, + // Next.js SDK (ESM) + { + name: '@sentry/nextjs (client)', + path: 'packages/nextjs/build/esm/client/index.js', + import: createImport('init'), + ignore: ['next/router', 'next/constants'], + gzip: true, + limit: '45 KB', + }, + // SvelteKit SDK (ESM) + { + name: '@sentry/sveltekit (client)', + path: 'packages/sveltekit/build/esm/client/index.js', + import: createImport('init'), + ignore: ['$app/stores'], + gzip: true, + limit: '41 KB', + }, + // Node-Core SDK (ESM) + { + name: '@sentry/node-core', + path: 'packages/node-core/build/esm/index.js', + import: createImport('init'), + ignore: [...builtinModules, ...nodePrefixedBuiltinModules], + gzip: true, + limit: '51 KB', + }, + // Node SDK (ESM) + { + name: '@sentry/node', + path: 'packages/node/build/esm/index.js', + import: createImport('init'), + ignore: [...builtinModules, ...nodePrefixedBuiltinModules], + gzip: true, + limit: '156 KB', + }, + { + name: '@sentry/node - without tracing', + path: 'packages/node/build/esm/index.js', + import: createImport('initWithoutDefaultIntegrations', 'getDefaultIntegrationsWithoutPerformance'), + gzip: true, + limit: '95 KB', + ignore: [...builtinModules, ...nodePrefixedBuiltinModules], + modifyWebpackConfig: function (config) { + const webpack = require('webpack'); + + config.plugins.push( + new webpack.DefinePlugin({ + __SENTRY_TRACING__: false, + }), + ); + + config.optimization.minimize = true; + + return config; + }, + }, + // AWS SDK (ESM) + { + name: '@sentry/aws-serverless', + path: 'packages/aws-serverless/build/npm/esm/index.js', + import: createImport('init'), + ignore: [...builtinModules, ...nodePrefixedBuiltinModules], + gzip: true, + limit: '111 KB', + }, +]; + +function createImport(...args) { + return `{ ${args.join(', ')} }`; +} + +function createCDNPath(name) { + return `packages/browser/build/bundles/${name}`; +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 89a95c0d16fe..000000000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -sudo: false -language: node_js -node_js: - - "0.12" -script: - - node_modules/.bin/grunt test diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000000..3ad96b1733d5 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "augustocdias.tasks-shell-input", + "denoland.vscode-deno" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000000..87d8002f6e7b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,121 @@ +{ + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + // For available variables, visit: https://code.visualstudio.com/docs/editor/variables-reference + "version": "0.2.0", + "inputs": [ + { + // Get the name of the package containing the file in the active tab. + "id": "getPackageName", + "type": "command", + "command": "shellCommand.execute", + "args": { + // Get the current file's absolute path, chop off everything up to and including the repo's `packages` + // directory, then split on `/` and take the first entry + "command": "echo '${file}' | sed s/'.*sentry-javascript\\/packages\\/'// | grep --extended-regexp --only-matching --max-count 1 '[^\\/]+' | head -1", + "cwd": "${workspaceFolder}", + // normally `input` commands bring up a selector for the user, but given that there should only be one + // choice here, this lets us skip the prompt + "useSingleResult": true + } + } + ], + "configurations": [ + // Debug the ts-node script in the currently active window + { + "name": "Debug ts-node script (open file)", + "type": "pwa-node", + "cwd": "${workspaceFolder}/packages/${input:getPackageName}", + "request": "launch", + "runtimeExecutable": "yarn", + "runtimeArgs": ["ts-node", "-P", "${workspaceFolder}/tsconfig.dev.json", "${file}"], + "skipFiles": ["/**"], + "outFiles": ["${workspaceFolder}/**/*.js", "!**/node_modules/**"], + "sourceMaps": true, + "smartStep": true, + "internalConsoleOptions": "openOnSessionStart", + "outputCapture": "std" + }, + // Run rollup using the config file which is in the currently active tab. + { + "name": "Debug rollup (config from open file)", + "type": "pwa-node", + "cwd": "${workspaceFolder}/packages/${input:getPackageName}", + "request": "launch", + "runtimeExecutable": "yarn", + "runtimeArgs": ["rollup", "-c", "${file}"], + "skipFiles": ["/**"], + "outFiles": ["${workspaceFolder}/**/*.js", "!**/node_modules/**"], + "sourceMaps": true, + "smartStep": true, + "internalConsoleOptions": "openOnSessionStart", + "outputCapture": "std" + }, + // Run a specific test file in watch mode (must have file in currently active tab when hitting the play button). + // NOTE: If you try to run this and VSCode complains that the command `shellCommand.execute` can't be found, go + // install the recommended extension Tasks Shell Input. + { + "name": "Debug playwright tests (just open file)", + "type": "pwa-node", + "cwd": "${workspaceFolder}/packages/${input:getPackageName}", + "request": "launch", + "runtimeExecutable": "yarn", + "runtimeArgs": [ + // `nodemon` is basically `node --watch` + "nodemon", + // be default it only watches JS files, so have it watch TS files instead + "--ext", + "ts", + "${workspaceFolder}/node_modules/playwright/node_modules/.bin/playwright", + "test", + "${file}" + ], + "skipFiles": ["/**"], + "outFiles": ["${workspaceFolder}/**/*.js", "!**/node_modules/**"], + "sourceMaps": true, + "smartStep": true, + "internalConsoleOptions": "openOnSessionStart", + // show stdout and stderr output in the debug console + "outputCapture": "std" + }, + + // @sentry/nextjs - Run a specific integration test file + // Must have test file in currently active tab when hitting the play button, and must already have run `yarn` in test app directory + { + "name": "Debug @sentry/nextjs integration tests - just open file", + "type": "node", + "cwd": "${workspaceFolder}/packages/nextjs", + "request": "launch", + // since we're not using the normal test runner, we need to make sure we're using the current version of all local + // SDK packages and then manually rebuild the test app + "preLaunchTask": "Prepare nextjs integration test app for debugging", + // running `server.js` directly (rather than running the tests through yarn) allows us to skip having to reinstall + // dependencies on every new test run + "program": "${workspaceFolder}/packages/nextjs/test/integration/test/server.js", + "args": [ + "--debug", + // remove these two lines to run all integration tests + "--filter", + "${fileBasename}" + ], + + "skipFiles": ["/**"], + "sourceMaps": true, + // this controls which files are sourcemapped + "outFiles": [ + // our SDK code + "${workspaceFolder}/**/cjs/**/*.js", + // the built test app + "${workspaceFolder}/packages/nextjs/test/integration/.next/**/*.js", + "!**/node_modules/**" + ], + "resolveSourceMapLocations": [ + "${workspaceFolder}/**/cjs/**", + "${workspaceFolder}/packages/nextjs/test/integration/.next/**", + "!**/node_modules/**" + ], + "internalConsoleOptions": "openOnSessionStart" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000000..c3515b80ced8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,32 @@ +{ + "editor.tabSize": 2, + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "search.exclude": { + "**/node_modules/": true, + "**/build/": true, + "**/dist/": true, + "**/coverage/": true, + "**/yarn-error.log": true + }, + "typescript.tsdk": "./node_modules/typescript/lib", + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "yaml.schemas": { + "https://json.schemastore.org/github-workflow.json": ".github/workflows/**.yml" + }, + "eslint.workingDirectories": [ + { + "mode": "auto" + } + ], + "deno.enablePaths": ["packages/deno/test"], + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000000..636302bff454 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,13 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 for documentation about `tasks.json` syntax + "version": "2.0.0", + "tasks": [ + { + "label": "Prepare nextjs integration test app for VSCode debugger", + "type": "npm", + "script": "predebug", + "path": "packages/nextjs/test/integration/", + "detail": "Link the SDK (if not already linked) and build test app" + } + ] +} diff --git a/.yarnrc b/.yarnrc new file mode 100644 index 000000000000..d81643fe43f1 --- /dev/null +++ b/.yarnrc @@ -0,0 +1 @@ +env: NODE_OPTIONS --stack-trace-limit=10000 diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 7f1ae4232176..000000000000 --- a/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -https://github.com/getsentry/raven-js/graphs/contributors diff --git a/CHANGELOG.md b/CHANGELOG.md index ae84fb6a4d75..b6d065c6c800 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,196 +1,2264 @@ # Changelog -## 2.0.5 -* BUGFIX: Fixed exception thrown by React Native plugin. See: https://github.com/getsentry/raven-js/issues/468 -* BUGFIX: Fixed "pre-built JavaScript" warning when loading Raven.js via Webpack: https://github.com/getsentry/raven-js/issues/465 - -## 2.0.4 -* BUGFIX: Fixed bug where Raven.VERSION was not set when required as a CommonJS module. - -## 2.0.2 -* BUGFIX: Fixed bug where wrapped requestAnimationFrame didn't return callback ID. See: https://github.com/getsentry/raven-js/pull/460 - -## 2.0.1 -* BUGFIX: Fixed bug where unwrapped errors might be suppressed. See: https://github.com/getsentry/raven-js/pull/447 - -## 2.0.0 - -* CHANGE: Raven.js now wraps functions passed to timer functions, event listeners, and XMLHttpRequest handlers -* CHANGE: Removed jQuery, Backbone, and native plugins (now handled inside raven.js) -* CHANGE: Default HTTP transport changed from `Image` GET to `XMLHttpRequest` POST (w/ CORS) -* CHANGE: When using CommonJS, plugins are initialized via `Raven.addPlugin(require('raven-js/plugins/ember'))` -* CHANGE: Raven builds are generated using Browserify -* NEW: Integration tests (/test/integration/index.html) - -## 1.3.0 -* CHANGE: `console` plugin will now send all arguments as an `extra` value. See: https://github.com/getsentry/raven-js/pull/398 -* CHANGE: Bump to v7 of the Sentry API spec. This now requires a Sentry 7.7.0+ https://github.com/getsentry/raven-js/pull/403 -* CHANGE: Revamp of AngularJS plugin. Please see documentation. See: https://github.com/getsentry/raven-js/pull/405 -* CHANGE: `Raven.debug` now defaults to `false`. https://github.com/getsentry/raven-js/commit/dc142b88f0c4953f54cb3754f9015d95ada55ba0 -* BUGFIX: `Raven.wrap` now correctly preserves `prototype`. See: https://github.com/getsentry/raven-js/issues/401 and https://github.com/getsentry/raven-js/pull/402 -* NEW: `serverName` config option. https://github.com/getsentry/raven-js/pull/404 -* NEW: Experimental support for React Native added. - -## 1.2.0 -* BUGFIX: Error in cases where a `document` context doesn't exist. See: https://github.com/getsentry/raven-js/pull/383 -* BUGFIX: Trailing comma when using unminified dist which affects IE9. See: https://github.com/getsentry/raven-js/pull/385 -* NEW: Add ability to swap in a custom transport. Adds `Raven.setTransport`, and `transport` option to config. Docs: https://docs.getsentry.com/hosted/clients/javascript/config/ -* CHANGE: Always expose `Raven` to `window`. Please call `Raven.noConflict()` if you want it restored to what it was. See: https://github.com/getsentry/raven-js/pull/393 -* DEPRECATED: Replace `Raven.setReleaseContext` with `Raven.setRelease`. -* NEW: Add `Raven.clearContext()` to empty all of the context. -* NEW: Add `Raven.getContext()` to get a copy of the current context. -* NEW: `Raven.set{Extra,Tags}Context(ctx)` now merges with existing values instead of overwriting. -* NEW: Add `Raven.addPlugin()` to register a plugin to be initialized when installed. -* NEW: Plugins are now initialized and loaded when calling `Raven.install()`. This avoid some race conditions with load order. +## Unreleased -## 1.1.22 - -* Fix another outstanding bug related to https://github.com/getsentry/raven-js/issues/377 that wasn't fully resolved with 1.1.21 -* Laid groundwork for pluggable transports, but not ready for public consumption yet - -## 1.1.21 +- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott -* Fix a bug where calling `captureException` before calling `Raven.config()` would trigger it's own exception. See: https://github.com/getsentry/raven-js/issues/377 +## 10.19.0 -## 1.1.20 +- feat(tracemetrics): Add trace metrics behind an experiments flag ([#17883](https://github.com/getsentry/sentry-javascript/pull/17883)) -* Wrap jquery's deferred[ resolveWith | rejectWith | notifyWith ] See: https://github.com/getsentry/raven-js/pull/268 -* Use window.crypto for uuid4 if present. See: https://github.com/getsentry/raven-js/pull/349 -* Add winjs support. See: https://github.com/getsentry/raven-js/commit/b9a1292cbc9275fc9f9f1108ff3698cbd5ce2180 -* Fix calling `Raven.captureException` from browser console. See: https://github.com/getsentry/raven-js/issues/358 -* guard against document.location being null or undefined. See: https://github.com/getsentry/raven-js/pull/357 -* Change error message format to match other clients. See: https://github.com/getsentry/raven-js/commit/64ca528b1b066ec7cdb5ef38e755c445f16ebef7 -* Don't require a user in the DSN. See: https://github.com/getsentry/raven-js/pull/361 -* Add `crossOrigin` option. See: https://github.com/getsentry/raven-js/pull/362 -* Avoid recursing when using the `console` plugin. See: https://github.com/getsentry/raven-js/commit/f92ff9de538f331a291af4a7d302202e587aaae5 +
+ Internal Changes -## 1.1.19 +- chore: add info latest release for the cursor release command ([#17876](https://github.com/getsentry/sentry-javascript/pull/17876)) -* Use more compliant way of creating an Image in the dom. See: https://github.com/getsentry/raven-js/pull/334 -* `String` objects weren't getting identified as a string. See: https://github.com/getsentry/raven-js/pull/336 -* Expose getter/setter for dataCallback and shouldSendCallback -* Better handle if/when the dataCallback returns garbage -* Fix support for nodeunit. See: https://github.com/getsentry/raven-js/pull/338 -* Fix `console.warn` sending as a `warning` level to server. See: https://github.com/getsentry/raven-js/issues/342 -* Improve the capture of unhandled errors from promises in Ember plugin. See: https://github.com/getsentry/raven-js/pull/330 +
-## 1.1.18 +## 10.18.0 -* Fixed a trailing comma which would make IE8 cry. This affects the uncompressed builds only. Compressed builds were unaffected. See: https://github.com/getsentry/raven-js/pull/333 +### Important Changes -## 1.1.17 +- **feat(node): `pino` integration ([#17584](https://github.com/getsentry/sentry-javascript/pull/17584))** -* Better support for Angular errors. See: https://github.com/getsentry/raven-js/pull/238 -* Allow setting truncate length through `globalOptions.maxMessageLength`. See: https://github.com/getsentry/raven-js/pull/246 -* Fixed the pattern for parsing gecko stacktraces. See: https://github.com/getsentry/raven-js/pull/252 -* Browserify support. See: https://github.com/getsentry/raven-js/pull/253, https://github.com/getsentry/raven-js/pull/260, https://github.com/getsentry/raven-js/pull/261 -* Start tracking `session:duration` automatically as metadata. -* Fix globalOptions overwrite. See: https://github.com/getsentry/raven-js/pull/264 -* Better cross origin support. See: https://github.com/getsentry/raven-js/pull/276 -* Better anonymous function support in Chrome stack trace parsing. See: https://github.com/getsentry/raven-js/pull/290, https://github.com/getsentry/raven-js/pull/294 -* Remove deprecated `site` param. -* New `Raven.isSetup()`. See: https://github.com/getsentry/raven-js/pull/309 -* Better backbone.js support. See: https://github.com/getsentry/raven-js/pull/307 -* `ignoreErrors` now also is applied to `captureMessage()`. See: https://github.com/getsentry/raven-js/pull/312 -* Capture unhandled errors from promises in Ember. See: https://github.com/getsentry/raven-js/pull/319 -* Add new support for `releases`. See: https://github.com/getsentry/raven-js/issues/325 + This release adds a new `pino` integration for Node.js, enabling Sentry to capture logs from the Pino logging library. -## 1.1.16 +- **feat: Remove @sentry/pino-transport package ([#17851](https://github.com/getsentry/sentry-javascript/pull/17851))** -* Fixed a bug that was preventing stack frames from `raven.js` from being hidden correctly. See: https://github.com/getsentry/raven-js/pull/216 -* Fixed an IE bug with the `console` plugin. See: https://github.com/getsentry/raven-js/issues/217 -* Added support for `chrome-extension://` protocol in Chrome in stack traces. -* Added `setExtraContext` and `setTagsContext`. See: https://github.com/getsentry/raven-js/pull/219 -* Renamed `setUser` to `setUserContext` to match. `setUser` still exists, but will be deprecated in a future release. -* New `backbone.js` plugin. See: https://github.com/getsentry/raven-js/pull/220 -* Added support for `chrome://` protocol in Firefox in stack traces. See: https://github.com/getsentry/raven-js/pull/225 -* Ignore more garbage from IE cross origin errors. See: https://github.com/getsentry/raven-js/pull/224 -* Added `Raven.debug` to prevent logging to `console` when `false`. Defaults to `true` for backwards compatability. See: https://github.com/getsentry/raven-js/pull/229 -* Prevent calling `Raven.config()` or `Raven.install()` twice. See: https://github.com/getsentry/raven-js/pull/233 + The `@sentry/pino-transport` package has been removed. Please use the new `pino` integration in `@sentry/node` instead. -## 1.1.15 +- **feat(node-core): Extend onnhandledrejection with ignore errors option ([#17736](https://github.com/getsentry/sentry-javascript/pull/17736))** -* Fix issues if a non-string were passed to `Raven.captureMessage` and non-Error objects were passed to `Raven.captureException`. + Added support for selectively suppressing specific errors with configurable logging control in onnhandledrejection integration. -## 1.1.14 +### Other Changes -* Only filter normal Error objects without a message, not all of them. Turns out, people throw errors like this. Ahem, Underscore.js. See: https://github.com/jashkenas/underscore/pull/1589/files +- feat(core): Rename vercelai.schema to gen_ai.request.schema ([#17850](https://github.com/getsentry/sentry-javascript/pull/17850)) +- feat(core): Support stream responses and tool calls for Google GenAI ([#17664](https://github.com/getsentry/sentry-javascript/pull/17664)) +- feat(nextjs): Attach headers using client hook ([#17831](https://github.com/getsentry/sentry-javascript/pull/17831)) +- fix(core): Keep all property values in baggage header ([#17847](https://github.com/getsentry/sentry-javascript/pull/17847)) +- fix(nestjs): Add support for Symbol as event name ([#17785](https://github.com/getsentry/sentry-javascript/pull/17785)) +- fix(nuxt): include `sentry.client.config.ts` in nuxt app types ([#17830](https://github.com/getsentry/sentry-javascript/pull/17830)) +- fix(react-router): Fix type for `OriginalHandleRequest` with middleware ([#17870](https://github.com/getsentry/sentry-javascript/pull/17870)) -## 1.1.13 +
+ Internal Changes -* Fixed a unicode issue in the previous release. +- chore: Add external contributor to CHANGELOG.md ([#17866](https://github.com/getsentry/sentry-javascript/pull/17866)) +- chore(deps): Bump @sentry/cli from 2.53.0 to 2.56.0 ([#17819](https://github.com/getsentry/sentry-javascript/pull/17819)) +- chore(deps): Bump axios in browser integration tests ([#17839](https://github.com/getsentry/sentry-javascript/pull/17839)) +- chore(deps): Bump nestjs in integration tests ([#17840](https://github.com/getsentry/sentry-javascript/pull/17840)) -## 1.1.12 +
-* Fix a bug using the `console` plugin with older IE. See: https://github.com/getsentry/raven-js/pull/192 -* Added initial `ember.js` plugin for early testing and feedback. -* Added initial `angular.js` plugin for early testing and feedback. -* Fixed an issue with the `require.js` plugin basically not working at all. See: https://github.com/getsentry/raven-js/commit/c2a2e2672a2a61a5a07e88f24a9c885f6dba57ae -* Got rid of `Raven.afterLoad` and made it internal only. -* `Raven.TraceKit` is now internal only. -* Truncate message length to a max of 100 characters becasue angular.js sucks and generates stupidly large error messages. +Work in this release was contributed by @stefanvanderwolf. Thank you for your contribution! -## 1.1.11 +## 10.17.0 -* Capture column number from FireFox -* Fix propagation of extra options through `captureException`, see: https://github.com/getsentry/raven-js/pull/189 -* Fix a minor bug that causes TraceKit to blow up of someone passes something dumb through `window.onerror` +### Important Changes -## 1.1.10 +- **feat(nuxt): Implement server middleware instrumentation ([#17796](https://github.com/getsentry/sentry-javascript/pull/17796))** -* A falsey DSN value disables Raven without yelling about an invalid DSN. + This release introduces instrumentation for Nuxt middleware, ensuring that all middleware handlers are automatically wrapped with tracing and error reporting functionality. -## 1.1.9 +- **fix(aws-serverless): Take `http_proxy` into account when choosing + `useLayerExtension` default ([#17817](https://github.com/getsentry/sentry-javascript/pull/17817))** -* Added `Raven.lastEventId()` to get back the Sentry event id. See: http://raven-js.readthedocs.org/en/latest/usage/index.html#getting-back-an-event-id -* Fixed a bug in the `console` plugin. See: https://github.com/getsentry/raven-js/pull/181 -* Provide a way out of deep wrapping arguments. See: https://github.com/getsentry/raven-js/pull/182 -* `Raven.uninstall()` actually removes the patched `window.onerror`. -* No more globally exposed `TraceKit`! + The default setting for `useLayerExtension` now considers the `http_proxy` environment variable. + When `http_proxy` is set, `useLayerExtension` will be off by default. + If you use a `http_proxy` but would still like to make use of the Sentry Lambda extension, exempt `localhost` in a `no_proxy` environment variable. -## 1.1.8 +### Other Changes -* Fixed a bug in IE8. See: https://github.com/getsentry/raven-js/pull/179 +- feat(node): Split up http integration into composable parts ([#17524](https://github.com/getsentry/sentry-javascript/pull/17524)) +- fix(core): Remove check and always respect ai.telemetry.functionId for Vercel AI gen spans ([#17811](https://github.com/getsentry/sentry-javascript/pull/17811)) +- doc(core): Fix outdated JSDoc in `beforeSendSpan` ([#17815](https://github.com/getsentry/sentry-javascript/pull/17815)) -## 1.1.4-1.1.7 +
+ Internal Changes -These were a bunch of super small incremental updates trying to get better integration and better support inside Sentry itself. +- ci: Do not run dependabot on e2e test applications ([#17813](https://github.com/getsentry/sentry-javascript/pull/17813)) +- docs: Reword changelog for google gen ai integration ([#17805](https://github.com/getsentry/sentry-javascript/pull/17805)) -* Culprit determined from the src url of the offending script, not the url of the page. -* Send Sentry the frames in the right order. They were being sent in reverse. Somehow nobody noticed this. -* Support for Chrome's new window.onerror api. See: https://github.com/getsentry/raven-js/issues/172 +
-## 1.1.3 +## 10.16.0 -* When loading with an AMD loader present, do not automatically call `Raven.noConflict()`. This was causing issues with using plugins. See: https://github.com/getsentry/raven-js/pull/165 -* https://github.com/getsentry/raven-js/pull/168 +- feat(logs): Add internal `replay_is_buffering` flag ([#17752](https://github.com/getsentry/sentry-javascript/pull/17752)) +- feat(react-router): Update loadContext type to be compatible with middleware ([#17758](https://github.com/getsentry/sentry-javascript/pull/17758)) +- feat(replay/logs): Only attach sampled replay Ids to logs ([#17750](https://github.com/getsentry/sentry-javascript/pull/17750)) +- fix(browser): Use current start timestamp for CLS span when CLS is 0 ([#17800](https://github.com/getsentry/sentry-javascript/pull/17800)) +- fix(core): Prevent `instrumentAnthropicAiClient` breaking MessageStream api ([#17754](https://github.com/getsentry/sentry-javascript/pull/17754)) +- fix(nextjs): Don't use chalk in turbopack config file ([#17806](https://github.com/getsentry/sentry-javascript/pull/17806)) +- fix(react): Do not send additional navigation span on pageload ([#17799](https://github.com/getsentry/sentry-javascript/pull/17799)) -## 1.1.2 +
+ Internal Changes -* An invalid DSN will now raise a RavenConfigError instead of some cryptic error -* Will raise a RavenConfigError when supplying the private key part of the DSN since this isn't applicable for raven.js and is harmful to include -* https://github.com/getsentry/raven-js/issues/128 +- build(aws): Ensure AWS build cache does not keep old files ([#17776](https://github.com/getsentry/sentry-javascript/pull/17776)) +- chore: Add `publish_release` command ([#17797](https://github.com/getsentry/sentry-javascript/pull/17797)) +- ref(aws-serverless): Add resolution for `import-in-the-middle` when building the Lambda layer ([#17780](https://github.com/getsentry/sentry-javascript/pull/17780)) +- ref(aws-serverless): Improve README with better examples ([#17787](https://github.com/getsentry/sentry-javascript/pull/17787)) +- ref(core): Improve promise buffer ([#17788](https://github.com/getsentry/sentry-javascript/pull/17788)) +- Revert "test(e2e): Pin `import-in-the-middle@1.14.2` due to `@vercel/nft` incompatibility ([#17777](https://github.com/getsentry/sentry-javascript/pull/17777))" (#17784) +- test(e2e): Pin `import-in-the-middle@1.14.2` due to `@vercel/nft` incompatibility ([#17777](https://github.com/getsentry/sentry-javascript/pull/17777)) +- test(nextjs): Add route handler tests for turbopack ([#17515](https://github.com/getsentry/sentry-javascript/pull/17515)) +- test(react-router): Test v8 middleware ([#17783](https://github.com/getsentry/sentry-javascript/pull/17783)) -## 1.1.1 +
-* Fixed a bug in parsing some DSNs. See: https://github.com/getsentry/raven-js/issues/160 +## 10.15.0 -## 1.1.0 +### Important Changes -### Plugins -If you're upgrading from 1.0.x, 2 "plugins" were included with the package. These 2 plugins are now stripped out of core and included as the `jquery` and `native` plugins. If you'd like to start using 1.1.0 and maintain existing functionality, you'll want to use: http://cdn.ravenjs.com/1.1.0/jquery,native/raven.min.js For a list of other plugins, checkout http://ravenjs.com +- **feat(cloudflare): Add honoIntegration with error-filtering function ([#17743](https://github.com/getsentry/sentry-javascript/pull/17743))** -### ravenjs.com -A new website dedicated to helping you compile a custom build of raven.js + This release adds a `honoIntegration` to `@sentry/cloudflare`, which exposes a `shouldHandleError` function that lets you define which errors in `onError` should be captured. + By default, Sentry captures exceptions with `error.status >= 500 || error.status <= 299`. -### whitelistUrls -`whitelistUrls` are recommended over `ignoreUrls`. `whitelistUrls` drastically helps cut out noisy error messages from other scripts running on your site. + The integration is added by default, and it's possible to modify this behavior like this: -### Misc -* `ignoreUrls`, `ignoreErrors`, `includePaths` have all been unified to accept both a regular expression and strings to avoid confusion and backwards compatability -* `Raven.wrap` recursively wraps arguments -* Events are dispatched when an exception is received, recorded or failed sending to Sentry -* Support newer Sentry protocol which allows smaller packets -* Allow loading raven async with RavenConfig -* Entirely new build system with Grunt -* `options.collectWindowErrors` to tell Raven to ignore window.onerror + ```js + integrations: [ + honoIntegration({ + shouldHandleError: (err) => true; // always capture exceptions in onError + }) + ] + ``` + +- **feat(node): Add instrumentation for hono handler ([#17428](https://github.com/getsentry/sentry-javascript/pull/17428))** + +This PR enhances the Hono integration by adding comprehensive handler instrumentation, error handling capabilities. + +- **feat(aws): Enable Lambda extension by default when using the Lamba layer ([#17684](https://github.com/getsentry/sentry-javascript/pull/17684))** + +- **feat(browser): Add `setActiveSpanInBrowser` to set an active span in the browser ([#17714](https://github.com/getsentry/sentry-javascript/pull/17714))** + +This PR adds a feature to the browser SDKs only: Making an inactive span active. We do this to enable use cases where having a span only being active in the callback is not practical. + +### Other Changes + +- fix(browser): Improve handling of `0` and `undefined` resource timing values ([#17751](https://github.com/getsentry/sentry-javascript/pull/17751)) +- ref(nextjs): Display build compatibility warning for webpack ([#17746](https://github.com/getsentry/sentry-javascript/pull/17746)) + +
+ Internal Changes + +- docs: Reword changelog for google gen ai instrumentation ([#17753](https://github.com/getsentry/sentry-javascript/pull/17753)) +- build: Add `@typescript-eslint/no-unnecessary-type-assertion` rule ([#17728](https://github.com/getsentry/sentry-javascript/pull/17728)) +- build: Update TS target to `es2020` everywhere ([#17709](https://github.com/getsentry/sentry-javascript/pull/17709)) +- chore: Add external contributor to CHANGELOG.md ([#17745](https://github.com/getsentry/sentry-javascript/pull/17745)) + +
+ +Work in this release was contributed by @Karibash. Thank you for your contribution! + +## 10.14.0 + +### Important Changes + +- **feat(cloudflare,vercel-edge): Add support for Google Gen AI instrumentation ([#17723](https://github.com/getsentry/sentry-javascript/pull/17723))** + + The SDK now supports manually instrumenting Google's Gen AI operations in Cloudflare Workers and Vercel Edge Runtime environments, providing insights into your AI operations. You can use `const wrappedClient = Sentry.instrumentGoogleGenAIClient(genAiClient)` to get an instrumented client. + +### Other Changes + +- fix(nextjs): Display updated turbopack warnings ([#17737](https://github.com/getsentry/sentry-javascript/pull/17737)) +- ref(core): Wrap isolationscope in `WeakRef` when storing it on spans ([#17712](https://github.com/getsentry/sentry-javascript/pull/17712)) + +
+ Internal Changes + +- test(node): Avoid using specific port for node-integration-tests ([#17729](https://github.com/getsentry/sentry-javascript/pull/17729)) +- test(nuxt): Update Nuxt version and add Nitro $fetch test ([#17713](https://github.com/getsentry/sentry-javascript/pull/17713)) + +
+ +## 10.13.0 + +### Important Changes + +- **feat(browser): Add option to explicitly end pageload span via `reportPageLoaded()` ([#17697](https://github.com/getsentry/sentry-javascript/pull/17697))** + + With this release you can take manual control of ending the pageload span. Usually this span is ended automatically by the SDK, based on a period of inactivity after the initial page was loaded in the browser. If you want full control over the pageload duration, you can tell Sentry, when your page was fully loaded: + + ```js + Sentry.init({ + //... + integrations: [ + // 1. Enable manual pageload reporting + Sentry.browserTracingIntegration({ enableReportPageLoaded: true }), + ], + }); + + // 2. Whenever you decide the page is loaded, call: + Sentry.reportPageLoaded(); + ``` + + Note that if `Sentry.reportPageLoaded()` is not called within 30 seconds of the initial pageload (or whatever value the `finalTimeout` option is set to), the pageload span will be ended automatically. + +- **feat(core,node): Add instrumentation for `GoogleGenAI` ([#17625](https://github.com/getsentry/sentry-javascript/pull/17625))** + + The SDK now automatically instruments the `@google/genai` package to provide insights into your AI operations. + +- **feat(nextjs): Promote `useRunAfterProductionCompileHook` to non-experimental build option ([#17721](https://github.com/getsentry/sentry-javascript/pull/17721))** + + The `useRunAfterProductionCompileHook` option is no longer experimental and is now a stable build option for Next.js projects. + +- **feat(nextjs): Use `afterProductionCompile` hook for webpack builds ([#17655](https://github.com/getsentry/sentry-javascript/pull/17655))** + + Next.js projects using webpack can opt-in to use the `useRunAfterProductionCompileHook` hook for source map uploads. + +- **feat(nextjs): Flip default value for `useRunAfterProductionCompileHook` for Turbopack builds ([#17722](https://github.com/getsentry/sentry-javascript/pull/17722))** + + The `useRunAfterProductionCompileHook` option is now enabled by default for Turbopack builds, enabling automated source map uploads. + +- **feat(node): Do not drop 300 and 304 status codes by default ([#17686](https://github.com/getsentry/sentry-javascript/pull/17686))** + + HTTP transactions with 300 and 304 status codes are now captured by default, providing better visibility into redirect and caching behavior. + +### Other Changes + +- feat(core): Add logger to core and allow scope to be passed log methods ([#17698](https://github.com/getsentry/sentry-javascript/pull/17698)) +- feat(core): Allow to pass `onSuccess` to `handleCallbackErrors` ([#17679](https://github.com/getsentry/sentry-javascript/pull/17679)) +- feat(core): Create template attributes in `consoleLoggingIntegration` ([#17703](https://github.com/getsentry/sentry-javascript/pull/17703)) +- feat(deps): bump @sentry/cli from 2.52.0 to 2.53.0 ([#17652](https://github.com/getsentry/sentry-javascript/pull/17652)) +- feat(node): Add extra platforms to `os` context ([#17720](https://github.com/getsentry/sentry-javascript/pull/17720)) +- fix(browser): Ensure idle span duration is adjusted when child spans are ignored ([#17700](https://github.com/getsentry/sentry-javascript/pull/17700)) +- fix(core): Ensure builtin stack frames don't affect `thirdPartyErrorFilterIntegration` ([#17693](https://github.com/getsentry/sentry-javascript/pull/17693)) +- fix(core): Fix client hook edge cases around multiple callbacks ([#17706](https://github.com/getsentry/sentry-javascript/pull/17706)) +- fix(nextjs): Enable fetch span when OTel setup is skipped ([#17699](https://github.com/getsentry/sentry-javascript/pull/17699)) +- fix(node): Fix `this` context for vercel AI instrumentation ([#17681](https://github.com/getsentry/sentry-javascript/pull/17681)) + +
+ Internal Changes + +- chore: Add external contributor to CHANGELOG.md ([#17725](https://github.com/getsentry/sentry-javascript/pull/17725)) +- chore: Add link to build and test icon in readme ([#17719](https://github.com/getsentry/sentry-javascript/pull/17719)) +- chore(nuxt): Bump Vite and Rollup plugins ([#17671](https://github.com/getsentry/sentry-javascript/pull/17671)) +- chore(repo): Add changelog entry for `reportPageLoaded` ([#17724](https://github.com/getsentry/sentry-javascript/pull/17724)) +- ci: Fix lookup of changed E2E test apps ([#17707](https://github.com/getsentry/sentry-javascript/pull/17707)) +- ci(test-matrix): Add logs for `getTestMatrix` ([#17673](https://github.com/getsentry/sentry-javascript/pull/17673)) +- ref: Avoid some usage of `SyncPromise` where not needed ([#17641](https://github.com/getsentry/sentry-javascript/pull/17641)) +- ref(core): Add debug log when dropping a span via `ignoreSpans` ([#17692](https://github.com/getsentry/sentry-javascript/pull/17692)) +- ref(core): Avoid looking up anthropic-ai integration options ([#17694](https://github.com/getsentry/sentry-javascript/pull/17694)) +- ref(core): Streamline `module_metadata` assignment and cleanup functions ([#17696](https://github.com/getsentry/sentry-javascript/pull/17696)) +- ref(remix): Avoid unnecessary error wrapping `HandleDocumentRequestFunction` ([#17680](https://github.com/getsentry/sentry-javascript/pull/17680)) +- Revert "[Gitflow] Merge master into develop" + +
+ +Work in this release was contributed by @Olexandr88. Thank you for your contribution! + +## 10.12.0 + +### Important Changes + +- **ref: Add and Adjust error event `mechanism` values** + + This release includes a variety of changes aimed at setting the `mechanism` field on errors captured automatically by the Sentry SDKs. [The intention](https://github.com/getsentry/sentry-javascript/issues/17212) is to clearly mark which instrumentation captured an error. In addition, some instrumentations previously did not yet annotate the error as handled or unhandled which this series of PRs corrects as well. + +
+ Relevant PRs + +
+ + Released in `10.12.0`: + - ref(angular): Adjust ErrorHandler event mechanism ([#17608](https://github.com/getsentry/sentry-javascript/pull/17608)) + - ref(astro): Adjust `mechanism` on error events captured by astro middleware ([#17613](https://github.com/getsentry/sentry-javascript/pull/17613)) + - ref(aws-severless): Slightly adjust aws-serverless mechanism type ([#17614](https://github.com/getsentry/sentry-javascript/pull/17614)) + - ref(bun): Adjust `mechanism` of errors captured in Bun.serve ([#17616](https://github.com/getsentry/sentry-javascript/pull/17616)) + - ref(cloudflare): Adjust event `mechanisms` and durable object origin ([#17618](https://github.com/getsentry/sentry-javascript/pull/17618)) + - ref(core): Adjust `mechanism` in `captureConsoleIntegration` ([#17633](https://github.com/getsentry/sentry-javascript/pull/17633)) + - ref(core): Adjust MCP server error event `mechanism` ([#17622](https://github.com/getsentry/sentry-javascript/pull/17622)) + - ref(core): Simplify `linkedErrors` mechanism logic ([#17600](https://github.com/getsentry/sentry-javascript/pull/17600)) + - ref(deno): Adjust `mechanism` of errors caught by `globalHandlersIntegration` ([#17635](https://github.com/getsentry/sentry-javascript/pull/17635)) + - ref(nextjs): Set more specific event `mechanism`s ([#17543](https://github.com/getsentry/sentry-javascript/pull/17543)) + - ref(node): Adjust mechanism of express, hapi and fastify error handlers ([#17623](https://github.com/getsentry/sentry-javascript/pull/17623)) + - ref(node-core): Add `mechanism` to cron instrumentations ([#17544](https://github.com/getsentry/sentry-javascript/pull/17544)) + - ref(node-core): Add more specific `mechanism.type` to worker thread errors from `childProcessIntegration` ([#17578](https://github.com/getsentry/sentry-javascript/pull/17578)) + - ref(node-core): Adjust `mechanism` of `onUnhandledRejection` and `onUnhandledException` integrations ([#17636](https://github.com/getsentry/sentry-javascript/pull/17636)) + - ref(node): Add mechanism to errors captured via connect and koa integrations ([#17579](https://github.com/getsentry/sentry-javascript/pull/17579)) + - ref(nuxt): Add and adjust `mechanism.type` in error events ([#17599](https://github.com/getsentry/sentry-javascript/pull/17599)) + - ref(react): Add mechanism to `reactErrorHandler` and adjust mechanism in `ErrorBoundary` ([#17602](https://github.com/getsentry/sentry-javascript/pull/17602)) + - ref(remix): Adjust event mechanism of `captureRemixServerException` ([#17629](https://github.com/getsentry/sentry-javascript/pull/17629)) + - ref(replay-internal): Add mechanism to error caught by `replayIntegration` in debug mode ([#17606](https://github.com/getsentry/sentry-javascript/pull/17606)) + - ref(solid): Add `mechanism` to error captured by `withSentryErrorBoundary` ([#17607](https://github.com/getsentry/sentry-javascript/pull/17607)) + - ref(solidstart): Adjust event mechanism in withServerActionInstrumentation ([#17637](https://github.com/getsentry/sentry-javascript/pull/17637)) + - ref(sveltekit): Adjust `mechanism` of error events ([#17646](https://github.com/getsentry/sentry-javascript/pull/17646)) + - ref(vue): Adjust mechanism in Vue error handler ([#17647](https://github.com/getsentry/sentry-javascript/pull/17647)) + +
+ + Released in `10.11.0`: + - ref(browser): Add more specific `mechanism.type` to errors captured by `httpClientIntegration` ([#17254](https://github.com/getsentry/sentry-javascript/pull/17254)) + - ref(browser): Set more descriptive `mechanism.type` in `browserApiErrorsIntergation` ([#17251](https://github.com/getsentry/sentry-javascript/pull/17251)) + - ref(core): Add `mechanism.type` to `trpcMiddleware` errors ([#17287](https://github.com/getsentry/sentry-javascript/pull/17287)) + - ref(core): Add more specific event `mechanism`s and span origins to `openAiIntegration` ([#17288](https://github.com/getsentry/sentry-javascript/pull/17288)) + - ref(nestjs): Add `mechanism` to captured errors ([#17312](https://github.com/getsentry/sentry-javascript/pull/17312)) + +
+ +- **feat(node) Ensure `prismaIntegration` works with Prisma 5 ([#17595](https://github.com/getsentry/sentry-javascript/pull/17595))** + +We used to require to pass in the v5 version of `@prisma/instrumentation` into `prismaIntegration({ prismaInstrumentation: new PrismaInstrumentation() })`, if you wanted to get full instrumentation for Prisma v5. However, it turns out this does not work on v10 of the SDK anymore, because `@prisma/instrumentation@5` requires OTEL v1. + +With this release, we dropped the requirement to configure anything to get v5 support of Prisma. You do not need to configure anything in the integration anymore, and can remove the dependency on `@prisma/instrumentation@5` if you had it in your application. You only need to configure the `tracing` preview feature [according to our docs](https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/prisma/). + +- **feat(deps): Update OpenTelemetry dependencies ([#17558](https://github.com/getsentry/sentry-javascript/pull/17558))** + - @opentelemetry/core bumped to ^2.1.0 + - @opentelemetry/context-async-hooks bumped to ^2.1.0 + - @opentelemetry/resources bumped to ^2.1.0 + - @opentelemetry/sdk-trace-base bumped to ^2.1.0 + - @opentelemetry/semantic-conventions bumped to ^1.37.0 + - @opentelemetry/instrumentation bumped to ^0.204.0 + - @opentelemetry/instrumentation-http bumped to ^0.204.0 + - @opentelemetry/instrumentation-amqplib bumped to ^0.51.0 + - @opentelemetry/instrumentation-aws-sdk bumped to ^0.59.0 + - @opentelemetry/instrumentation-connect bumped to ^0.48.0 + - @opentelemetry/instrumentation-dataloader bumped to ^0.22.0 + - @opentelemetry/instrumentation-express bumped to ^0.53.0 + - @opentelemetry/instrumentation-fs bumped from to ^0.24.0 + - @opentelemetry/instrumentation-generic-pool bumped to ^0.48.0 + - @opentelemetry/instrumentation-graphql bumped to ^0.52.0 + - @opentelemetry/instrumentation-hapi bumped to ^0.51.0 + - @opentelemetry/instrumentation-ioredis bumped to ^0.52.0 + - @opentelemetry/instrumentation-kafkajs bumped to ^0.14.0 + - @opentelemetry/instrumentation-knex bumped to ^0.49.0 + - @opentelemetry/instrumentation-koa bumped to ^0.52.0 + - @opentelemetry/instrumentation-lru-memoizer bumped to ^0.49.0 + - @opentelemetry/instrumentation-mongodb bumped from to ^0.57.0 + - @opentelemetry/instrumentation-mongoose bumped from to ^0.51.0 + - @opentelemetry/instrumentation-mysql bumped to ^0.50.0 + - @opentelemetry/instrumentation-mysql2 bumped to ^0.51.0 + - @opentelemetry/instrumentation-nestjs-core bumped to ^0.50.0 + - @opentelemetry/instrumentation-pg bumped to ^0.57.0 + - @opentelemetry/instrumentation-redis bumped to ^0.53.0 + - @opentelemetry/instrumentation-undici bumped to ^0.15.0 + - @prisma/instrumentation bumped to 6.15.0 + +### Other Changes + +- feat(browser): Add timing and status atttributes to resource spans ([#17562](https://github.com/getsentry/sentry-javascript/pull/17562)) +- feat(cloudflare,vercel-edge): Add support for Anthropic AI instrumentation ([#17571](https://github.com/getsentry/sentry-javascript/pull/17571)) +- feat(core): Add Consola integration ([#17435](https://github.com/getsentry/sentry-javascript/pull/17435)) +- feat(deps): Update OpenTelemetry dependencies ([#17569](https://github.com/getsentry/sentry-javascript/pull/17569)) +- feat(core): Export `TracesSamplerSamplingContext` type ([#17523](https://github.com/getsentry/sentry-javascript/pull/17523)) +- feat(deno): Add OpenTelemetry support and vercelAI integration ([#17445](https://github.com/getsentry/sentry-javascript/pull/17445)) +- feat(node-core): Remove experimental note from winston api ([#17626](https://github.com/getsentry/sentry-javascript/pull/17626)) +- feat(node): Ensure `prismaIntegration` works with Prisma v5 ([#17595](https://github.com/getsentry/sentry-javascript/pull/17595)) +- feat(node): Tidy existing ESM loader hook ([#17566](https://github.com/getsentry/sentry-javascript/pull/17566)) +- feat(sveltekit): Align build time options with shared type ([#17413](https://github.com/getsentry/sentry-javascript/pull/17413)) +- fix(core): Fix error handling when sending envelopes ([#17662](https://github.com/getsentry/sentry-javascript/pull/17662)) +- fix(browser): Always start navigation as root span ([#17648](https://github.com/getsentry/sentry-javascript/pull/17648)) +- fix(browser): Ensure propagated `parentSpanId` stays consistent during trace in TwP mode ([#17526](https://github.com/getsentry/sentry-javascript/pull/17526)) +- fix(cloudflare): Initialize once per workflow run and preserve scope for `step.do` ([#17582](https://github.com/getsentry/sentry-javascript/pull/17582)) +- fix(nextjs): Add edge polyfills for nextjs-13 in dev mode ([#17488](https://github.com/getsentry/sentry-javascript/pull/17488)) +- fix(nitro): Support nested `_platform` properties in Nitro 2.11.7+ ([#17596](https://github.com/getsentry/sentry-javascript/pull/17596)) +- fix(node): Preserve synchronous return behavior for streamText and other methods for AI ([#17580](https://github.com/getsentry/sentry-javascript/pull/17580)) +- ref(node): Inline types imported from `shimmer` ([#17597](https://github.com/getsentry/sentry-javascript/pull/17597)) - ref(nuxt): Add and adjust `mechanism.type` in error events ([#17599](https://github.com/getsentry/sentry-javascript/pull/17599)) +- ref(browser): Improve `fetchTransport` error handling ([#17661](https://github.com/getsentry/sentry-javascript/pull/17661)) + +
+ Internal Changes + +- chore: Add changelog note about mechanism changes ([#17632](https://github.com/getsentry/sentry-javascript/pull/17632)) +- chore(aws): Update README.md ([#17601](https://github.com/getsentry/sentry-javascript/pull/17601)) +- chore(deps): bump hono from 4.7.10 to 4.9.7 in /dev-packages/e2e-tests/test-applications/cloudflare-hono ([#17630](https://github.com/getsentry/sentry-javascript/pull/17630)) +- chore(deps): bump next from 14.2.25 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-app-dir ([#17627](https://github.com/getsentry/sentry-javascript/pull/17627)) +- chore(deps): bump next from 14.2.25 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-pages-dir ([#17620](https://github.com/getsentry/sentry-javascript/pull/17620)) +- chore(deps): bump next from 14.2.29 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-orpc ([#17494](https://github.com/getsentry/sentry-javascript/pull/17494)) +- chore(deps): bump next from 14.2.30 to 14.2.32 in /dev-packages/e2e-tests/test-applications/nextjs-14 ([#17628](https://github.com/getsentry/sentry-javascript/pull/17628)) +- chore(repo): Rename `.claude/settings.local.json` to `.claude/settings.json` ([#17591](https://github.com/getsentry/sentry-javascript/pull/17591)) +- docs(issue-template): Add note about prioritization ([#17590](https://github.com/getsentry/sentry-javascript/pull/17590)) +- ref(core): Streamline event processor handling ([#17634](https://github.com/getsentry/sentry-javascript/pull/17634)) +- test(angular): Bump TS version to 5.9.0 in Angular 20 e2e test ([#17605](https://github.com/getsentry/sentry-javascript/pull/17605)) +- test(nextjs): Remove Next 13 and pin Next 14 canary and latest tests ([#17577](https://github.com/getsentry/sentry-javascript/pull/17577)) +- test(react-router): Unflake `flushIfServerless` test ([#17610](https://github.com/getsentry/sentry-javascript/pull/17610)) + +
+ +## 10.11.0 + +### Important Changes + +- **feat(aws): Add experimental AWS Lambda extension for tunnelling events ([#17525](https://github.com/getsentry/sentry-javascript/pull/17525))** + + This release adds an experimental Sentry Lambda extension to the existing Sentry Lambda layer. Sentry events are now tunneled through the extension and then forwarded to Sentry. This has the benefit of reducing the request processing time. + + To enable it, set `_experiments.enableLambdaExtension` in your Sentry config like this: + + ```javascript + Sentry.init({ + dsn: '', + _experiments: { + enableLambdaExtension: true, + }, + }); + ``` + +### Other Changes + +- feat(core): Add replay id to logs ([#17563](https://github.com/getsentry/sentry-javascript/pull/17563)) +- feat(core): Improve error handling for Anthropic AI instrumentation ([#17535](https://github.com/getsentry/sentry-javascript/pull/17535)) +- feat(deps): bump @opentelemetry/instrumentation-ioredis from 0.51.0 to 0.52.0 ([#17557](https://github.com/getsentry/sentry-javascript/pull/17557)) +- feat(node): Add incoming request headers as OTel span attributes ([#17475](https://github.com/getsentry/sentry-javascript/pull/17475)) +- fix(astro): Ensure traces are correctly propagated for static routes ([#17536](https://github.com/getsentry/sentry-javascript/pull/17536)) +- fix(react): Remove `handleExistingNavigation` ([#17534](https://github.com/getsentry/sentry-javascript/pull/17534)) +- ref(browser): Add more specific `mechanism.type` to errors captured by `httpClientIntegration` ([#17254](https://github.com/getsentry/sentry-javascript/pull/17254)) +- ref(browser): Set more descriptive `mechanism.type` in `browserApiErrorsIntergation` ([#17251](https://github.com/getsentry/sentry-javascript/pull/17251)) +- ref(core): Add `mechanism.type` to `trpcMiddleware` errors ([#17287](https://github.com/getsentry/sentry-javascript/pull/17287)) +- ref(core): Add more specific event `mechanism`s and span origins to `openAiIntegration` ([#17288](https://github.com/getsentry/sentry-javascript/pull/17288)) +- ref(nestjs): Add `mechanism` to captured errors ([#17312](https://github.com/getsentry/sentry-javascript/pull/17312)) + +
+ Internal Changes + +- chore: Use proper `test-utils` dependency in workspace ([#17538](https://github.com/getsentry/sentry-javascript/pull/17538)) +- chore(test): Remove `geist` font ([#17541](https://github.com/getsentry/sentry-javascript/pull/17541)) +- ci: Check for stable lockfile ([#17552](https://github.com/getsentry/sentry-javascript/pull/17552)) +- ci: Fix running of only changed E2E tests ([#17551](https://github.com/getsentry/sentry-javascript/pull/17551)) +- ci: Remove project automation workflow ([#17508](https://github.com/getsentry/sentry-javascript/pull/17508)) +- test(node-integration-tests): pin ai@5.0.30 to fix test fails ([#17542](https://github.com/getsentry/sentry-javascript/pull/17542)) + +
+ +## 10.10.0 + +### Important Changes + +- **feat(browser): Add support for `propagateTraceparent` SDK option ([#17509](https://github.com/getsentry/sentry-javascript/pull/17509))** + +Adds support for a new browser SDK init option, `propagateTraceparent` for attaching a W3C compliant traceparent header to outgoing fetch and XHR requests, in addition to sentry-trace and baggage headers. More details can be found [here](https://develop.sentry.dev/sdk/telemetry/traces/#propagatetraceparent). + +- **feat(core): Add tool calls attributes for Anthropic AI ([#17478](https://github.com/getsentry/sentry-javascript/pull/17478))** + +Adds missing tool call attributes, we add gen_ai.response.tool_calls attribute for Anthropic AI, supporting both streaming and non-streaming requests. + +- **feat(nextjs): Use compiler hook for uploading turbopack sourcemaps ([#17352](https://github.com/getsentry/sentry-javascript/pull/17352))** + +Adds a new _experimental_ flag `_experimental.useRunAfterProductionCompileHook` to `withSentryConfig` for automatic source maps uploads when building a Next.js app with `next build --turbopack`. +When set we: + +- Automatically enable source map generation for turbopack client files (if not explicitly disabled) +- Upload generated source maps to Sentry at the end of the build by leveraging [a Next.js compiler hook](https://nextjs.org/docs/architecture/nextjs-compiler#runafterproductioncompile). + +### Other Changes + +- feat(feedback): Add more labels so people can configure Highlight and Hide labels ([#17513](https://github.com/getsentry/sentry-javascript/pull/17513)) +- fix(node): Add `origin` for OpenAI spans & test auto instrumentation ([#17519](https://github.com/getsentry/sentry-javascript/pull/17519)) + +## 10.9.0 + +### Important Changes + +- **feat(node): Update `httpIntegration` handling of incoming requests ([#17371](https://github.com/getsentry/sentry-javascript/pull/17371))** + +This version updates the handling of the Node SDK of incoming requests. Instead of relying on @opentelemetry/instrumentation-http, we now handle incoming request instrumentation internally, ensuring that we can optimize performance as much as possible and avoid interop problems. + +This change should not affect you, unless you're relying on very in-depth implementation details. Importantly, this also drops the `_experimentalConfig` option of the integration - this will no longer do anything. +Finally, you can still pass `instrumentation.{requestHook,responseHook,applyCustomAttributesOnSpan}` options, but they are deprecated and will be removed in v11. Instead, you can use the new `incomingRequestSpanHook` configuration option if you want to adjust the incoming request span. + +### Other Changes + +- feat(browser): Add replay.feedback CDN bundle ([#17496](https://github.com/getsentry/sentry-javascript/pull/17496)) +- feat(browser): Export `sendFeedback` from CDN bundles ([#17495](https://github.com/getsentry/sentry-javascript/pull/17495)) +- fix(astro): Ensure span name from `beforeStartSpan` isn't overwritten ([#17500](https://github.com/getsentry/sentry-javascript/pull/17500)) +- fix(browser): Ensure source is set correctly when updating span name in-place in `beforeStartSpan` ([#17501](https://github.com/getsentry/sentry-javascript/pull/17501)) +- fix(core): Only set template attributes on logs if parameters exist ([#17480](https://github.com/getsentry/sentry-javascript/pull/17480)) +- fix(nextjs): Fix parameterization for root catchall routes ([#17489](https://github.com/getsentry/sentry-javascript/pull/17489)) +- fix(node-core): Shut down OTel TraceProvider when calling `Sentry.close()` ([#17499](https://github.com/getsentry/sentry-javascript/pull/17499)) + +
+ Internal Changes + +- chore: Add `changelog` script back to package.json ([#17517](https://github.com/getsentry/sentry-javascript/pull/17517)) +- chore: Ensure prettier is run on all files ([#17497](https://github.com/getsentry/sentry-javascript/pull/17497)) +- chore: Ignore prettier commit for git blame ([#17498](https://github.com/getsentry/sentry-javascript/pull/17498)) +- chore: Remove experimental from Nuxt SDK package description ([#17483](https://github.com/getsentry/sentry-javascript/pull/17483)) +- ci: Capture overhead in node app ([#17420](https://github.com/getsentry/sentry-javascript/pull/17420)) +- ci: Ensure we fail on cancelled jobs ([#17506](https://github.com/getsentry/sentry-javascript/pull/17506)) +- ci(deps): bump actions/checkout from 4 to 5 ([#17505](https://github.com/getsentry/sentry-javascript/pull/17505)) +- ci(deps): bump actions/create-github-app-token from 2.0.6 to 2.1.1 ([#17504](https://github.com/getsentry/sentry-javascript/pull/17504)) +- test(aws): Improve reliability on CI ([#17502](https://github.com/getsentry/sentry-javascript/pull/17502)) + +
+ +## 10.8.0 + +### Important Changes + +- **feat(sveltekit): Add Compatibility for builtin SvelteKit Tracing ([#17423](https://github.com/getsentry/sentry-javascript/pull/17423))** + + This release makes the `@sentry/sveltekit` SDK compatible with SvelteKit's native [observability support](https://svelte.dev/docs/kit/observability) introduced in SvelteKit version `2.31.0`. + If you enable both, instrumentation and tracing, the SDK will now initialize early enough to set up additional instrumentation like database queries and it will pick up spans emitted from SvelteKit. + + We will follow up with docs how to set up the SDK soon. + For now, If you're on SvelteKit version `2.31.0` or newer, you can easily opt into the new feature: + 1. Enable [experimental tracing and instrumentation support](https://svelte.dev/docs/kit/observability) in `svelte.config.js`: + 2. Move your `Sentry.init()` call from `src/hooks.server.(js|ts)` to the new `instrumentation.server.(js|ts)` file: + + ```ts + // instrumentation.server.ts + import * as Sentry from '@sentry/sveltekit'; + + Sentry.init({ + dsn: '...', + // rest of your config + }); + ``` + + The rest of your Sentry config in `hooks.server.ts` (`sentryHandle` and `handleErrorWithSentry`) should stay the same. + + If you prefer to stay on the hooks-file based config for now, the SDK will continue to work as previously. + + Thanks to the Svelte team and @elliott-with-the-longest-name-on-github for implementing observability support and for reviewing our PR! + +### Other Changes + +- fix(react): Avoid multiple name updates on navigation spans ([#17438](https://github.com/getsentry/sentry-javascript/pull/17438)) + +
+ Internal Changes + +- test(profiling): Add tests for current state of profiling ([#17470](https://github.com/getsentry/sentry-javascript/pull/17470)) + +
+ +## 10.7.0 + +### Important Changes + +- **feat(cloudflare): Add `instrumentPrototypeMethods` option to instrument RPC methods for DurableObjects ([#17424](https://github.com/getsentry/sentry-javascript/pull/17424))** + +By default, `Sentry.instrumentDurableObjectWithSentry` will not wrap any RPC methods on the prototype. To enable wrapping for RPC methods, set `instrumentPrototypeMethods` to `true` or, if performance is a concern, a list of only the methods you want to instrument: + +```js +class MyDurableObjectBase extends DurableObject { + method1() { + // ... + } + + method2() { + // ... + } + + method3() { + // ... + } +} +// Export your named class as defined in your wrangler config +export const MyDurableObject = Sentry.instrumentDurableObjectWithSentry( + (env: Env) => ({ + dsn: "https://ac49b7af3017c458bd12dab9b3328bfc@o4508482761982032.ingest.de.sentry.io/4508482780987481", + tracesSampleRate: 1.0, + instrumentPrototypeMethods: ['method1', 'method3'], + }), + MyDurableObjectBase, +); +``` + +## Other Changes + +- feat(aws): Add support for streaming handlers ([#17463](https://github.com/getsentry/sentry-javascript/pull/17463)) +- feat(core): Stream responses Anthropic AI ([#17460](https://github.com/getsentry/sentry-javascript/pull/17460)) +- feat(deps): bump @opentelemetry/instrumentation-aws-sdk from 0.56.0 to 0.57.0 ([#17455](https://github.com/getsentry/sentry-javascript/pull/17455)) +- feat(deps): bump @opentelemetry/instrumentation-dataloader from 0.21.0 to 0.21.1 ([#17457](https://github.com/getsentry/sentry-javascript/pull/17457)) +- feat(deps): bump @opentelemetry/instrumentation-kafkajs from 0.12.0 to 0.13.0 ([#17469](https://github.com/getsentry/sentry-javascript/pull/17469)) +- feat(deps): bump @opentelemetry/instrumentation-mysql2 from 0.49.0 to 0.50.0 ([#17459](https://github.com/getsentry/sentry-javascript/pull/17459)) +- feat(deps): bump @prisma/instrumentation from 6.13.0 to 6.14.0 ([#17466](https://github.com/getsentry/sentry-javascript/pull/17466)) +- feat(deps): bump @sentry/cli from 2.51.1 to 2.52.0 ([#17458](https://github.com/getsentry/sentry-javascript/pull/17458)) +- feat(deps): bump @sentry/rollup-plugin from 4.1.0 to 4.1.1 ([#17456](https://github.com/getsentry/sentry-javascript/pull/17456)) +- feat(deps): bump @sentry/webpack-plugin from 4.1.0 to 4.1.1 ([#17467](https://github.com/getsentry/sentry-javascript/pull/17467)) +- feat(replay): Add option to skip `requestAnimationFrame` for canvas snapshots ([#17380](https://github.com/getsentry/sentry-javascript/pull/17380)) + +
+ Internal Changes + +- test(aws): Run E2E tests in all supported Node versions ([#17446](https://github.com/getsentry/sentry-javascript/pull/17446)) + +
+ +## 10.6.0 + +### Important Changes + +- **feat(node): Add Anthropic AI integration ([#17348](https://github.com/getsentry/sentry-javascript/pull/17348))** + +This release adds support for automatically tracing Anthropic AI SDK requests, providing better observability for AI-powered applications. + +- **fix(core): Instrument invoke_agent root span, and support Vercel `ai` v5 ([#17395](https://github.com/getsentry/sentry-javascript/pull/17395))** + +This release makes the Sentry `vercelAiIntegration` compatible with version 5 of Vercel `ai`. + +- **docs(nuxt): Remove beta notice ([#17400](https://github.com/getsentry/sentry-javascript/pull/17400))** + +The Sentry Nuxt SDK is now considered stable and no longer in beta! + +### Other Changes + +- feat(astro): Align options with shared build time options type ([#17396](https://github.com/getsentry/sentry-javascript/pull/17396)) +- feat(aws): Add support for automatic wrapping in ESM ([#17407](https://github.com/getsentry/sentry-javascript/pull/17407)) +- feat(node): Add an instrumentation interface for Hono ([#17366](https://github.com/getsentry/sentry-javascript/pull/17366)) +- fix(browser): Use `DedicatedWorkerGlobalScope` global object type in `registerWebWorker` ([#17447](https://github.com/getsentry/sentry-javascript/pull/17447)) +- fix(core): Only consider ingest endpoint requests when checking `isSentryRequestUrl` ([#17393](https://github.com/getsentry/sentry-javascript/pull/17393)) +- fix(node): Fix preloading of instrumentation ([#17403](https://github.com/getsentry/sentry-javascript/pull/17403)) + +
+ Internal Changes + +- chore: Add external contributor to CHANGELOG.md ([#17449](https://github.com/getsentry/sentry-javascript/pull/17449)) +- chore(deps): bump astro from 4.16.18 to 4.16.19 in /dev-packages/e2e-tests/test-applications/astro-4 ([#17434](https://github.com/getsentry/sentry-javascript/pull/17434)) +- test(e2e/firebase): Fix firebase e2e test failing due to outdated rules file ([#17448](https://github.com/getsentry/sentry-javascript/pull/17448)) +- test(nextjs): Fix canary tests ([#17416](https://github.com/getsentry/sentry-javascript/pull/17416)) +- test(nuxt): Don't rely on flushing for lowQualityTransactionFilter ([#17406](https://github.com/getsentry/sentry-javascript/pull/17406)) +- test(solidstart): Don't rely on flushing for lowQualityTransactionFilter ([#17408](https://github.com/getsentry/sentry-javascript/pull/17408)) + +
+ +## 10.5.0 + +- feat(core): better cause data extraction ([#17375](https://github.com/getsentry/sentry-javascript/pull/17375)) +- feat(deps): Bump @sentry/cli from 2.50.2 to 2.51.1 ([#17382](https://github.com/getsentry/sentry-javascript/pull/17382)) +- feat(deps): Bump @sentry/rollup-plugin and @sentry/vite-plugin from 4.0.2 to 4.1.0 ([#17383](https://github.com/getsentry/sentry-javascript/pull/17383)) +- feat(deps): Bump @sentry/webpack-plugin from 4.0.2 to 4.1.0 ([#17381](https://github.com/getsentry/sentry-javascript/pull/17381)) +- feat(node): Capture `SystemError` context and remove paths from message ([#17331](https://github.com/getsentry/sentry-javascript/pull/17331)) +- fix(nextjs): Inject Next.js version for dev symbolication ([#17379](https://github.com/getsentry/sentry-javascript/pull/17379)) +- fix(mcp-server): Add defensive patches for Transport edge cases ([#17291](https://github.com/getsentry/sentry-javascript/pull/17291)) + +
+ Internal Changes + +- chore(repo): Adjust "Publishing a Release" document to include internal changes section in changelog ([#17374](https://github.com/getsentry/sentry-javascript/pull/17374)) +- test(aws): Run E2E tests with AWS SAM ([#17367](https://github.com/getsentry/sentry-javascript/pull/17367)) +- test(node): Add tests for full http.server span attribute coverage ([#17373](https://github.com/getsentry/sentry-javascript/pull/17373)) + +
+ +Work in this release was contributed by @ha1fstack. Thank you for your contribution! + +## 10.4.0 + +### Important Changes + +- **fix(browser): Ensure IP address is only inferred by Relay if `sendDefaultPii` is `true`** + +This release includes a fix for a [behaviour change](https://docs.sentry.io/platforms/javascript/migration/v8-to-v9/#behavior-changes) +that was originally introduced with v9 of the SDK: User IP Addresses should only be added to Sentry events automatically, +if `sendDefaultPii` was set to `true`. + +However, the change in v9 required further internal adjustment, which should have been included in v10 of the SDK. +Unfortunately, the change did not make it into the initial v10 version but is now applied with `10.4.0`. +There is _no API_ breakage involved and hence it is safe to update. +However, after updating the SDK, events (errors, traces, replays, etc.) sent from the browser, will only include +user IP addresses, if you set `sendDefaultPii: true` in your `Sentry.init` options. + +We apologize for any inconvenience caused! + +- **feat(node): Add `ignoreStaticAssets` ([#17370](https://github.com/getsentry/sentry-javascript/pull/17370))** + +This release adds a new option to `httpIntegration` to ignore requests for static assets (e.g. `favicon.xml` or `robots.txt`). The option defaults to `true`, meaning that going forward, such requests will not be traced by default. You can still enable tracing for these requests by setting the option to `false`: + +```js +Sentry.init({ + integrations: [ + Sentry.httpIntegration({ + // defaults to true, set to false to enable traces for static assets + ignoreStaticAssets: false, + }), + ], +}); +``` + +### Other Changes + +- fix(nuxt): Do not drop parametrized routes ([#17357](https://github.com/getsentry/sentry-javascript/pull/17357)) + +
+ Internal Changes + +- ref(node): Split up incoming & outgoing http handling ([#17358](https://github.com/getsentry/sentry-javascript/pull/17358)) +- test(node): Enable additionalDependencies in integration runner ([#17361](https://github.com/getsentry/sentry-javascript/pull/17361)) + +
+ +## 10.3.0 + +- feat(core): MCP Server - Capture prompt results from prompt function calls (#17284) +- feat(bun): Export `skipOpenTelemetrySetup` option ([#17349](https://github.com/getsentry/sentry-javascript/pull/17349)) +- feat(sveltekit): Streamline build logs ([#17306](https://github.com/getsentry/sentry-javascript/pull/17306)) +- fix(browser): Handle data urls in errors caught by `globalHandlersIntegration` ([#17216](https://github.com/getsentry/sentry-javascript/pull/17216)) +- fix(browser): Improve navigation vs. redirect detection ([#17275](https://github.com/getsentry/sentry-javascript/pull/17275)) +- fix(react-router): Ensure source map upload fails silently if Sentry CLI fails ([#17081](https://github.com/getsentry/sentry-javascript/pull/17081)) +- fix(react): Add support for React Router sub-routes from `handle` ([#17277](https://github.com/getsentry/sentry-javascript/pull/17277)) + +## 10.2.0 + +### Important Changes + +- **feat(core): Add `ignoreSpans` option ([#17078](https://github.com/getsentry/sentry-javascript/pull/17078))** + +This release adds a new top-level `Sentry.init` option, `ignoreSpans`, that can be used as follows: + +```js +Sentry.init({ + ignoreSpans: [ + 'partial match', // string matching on the span name + /regex/, // regex matching on the span name + { + name: 'span name', + op: /http.client/, + }, + ], +}); +``` + +Spans matching the filter criteria will not be recorded. Potential child spans of filtered spans will be re-parented, if possible. + +- **feat(cloudflare,vercel-edge): Add support for OpenAI instrumentation ([#17338](https://github.com/getsentry/sentry-javascript/pull/17338))** + +Adds support for OpenAI manual instrumentation in `@sentry/cloudflare` and `@sentry/vercel-edge`. + +To instrument the OpenAI client, wrap it with `Sentry.instrumentOpenAiClient` and set recording settings. + +```js +import * as Sentry from '@sentry/cloudflare'; +import OpenAI from 'openai'; + +const openai = new OpenAI(); +const client = Sentry.instrumentOpenAiClient(openai, { recordInputs: true, recordOutputs: true }); + +// use the wrapped client +``` + +- **ref(aws): Remove manual span creation ([#17310](https://github.com/getsentry/sentry-javascript/pull/17310))** + +The `startTrace` option is deprecated and will be removed in a future major version. If you want to disable tracing, set `SENTRY_TRACES_SAMPLE_RATE` to `0.0`. instead. As of today, the flag does not affect traces anymore. + +### Other Changes + +- feat(astro): Streamline build logs ([#17301](https://github.com/getsentry/sentry-javascript/pull/17301)) +- feat(browser): Handles data URIs in chrome stack frames ([#17292](https://github.com/getsentry/sentry-javascript/pull/17292)) +- feat(core): Accumulate tokens for `gen_ai.invoke_agent` spans from child LLM calls ([#17281](https://github.com/getsentry/sentry-javascript/pull/17281)) +- feat(deps): Bump @prisma/instrumentation from 6.12.0 to 6.13.0 ([#17315](https://github.com/getsentry/sentry-javascript/pull/17315)) +- feat(deps): Bump @sentry/cli from 2.50.0 to 2.50.2 ([#17316](https://github.com/getsentry/sentry-javascript/pull/17316)) +- feat(deps): Bump @sentry/rollup-plugin from 4.0.0 to 4.0.2 ([#17317](https://github.com/getsentry/sentry-javascript/pull/17317)) +- feat(deps): Bump @sentry/webpack-plugin from 4.0.0 to 4.0.2 ([#17314](https://github.com/getsentry/sentry-javascript/pull/17314)) +- feat(nuxt): Do not inject trace meta-tags on cached HTML pages ([#17305](https://github.com/getsentry/sentry-javascript/pull/17305)) +- feat(nuxt): Streamline build logs ([#17308](https://github.com/getsentry/sentry-javascript/pull/17308)) +- feat(react-router): Add support for Hydrogen with RR7 ([#17145](https://github.com/getsentry/sentry-javascript/pull/17145)) +- feat(react-router): Streamline build logs ([#17303](https://github.com/getsentry/sentry-javascript/pull/17303)) +- feat(solidstart): Streamline build logs ([#17304](https://github.com/getsentry/sentry-javascript/pull/17304)) +- fix(nestjs): Add missing `sentry.origin` span attribute to `SentryTraced` decorator ([#17318](https://github.com/getsentry/sentry-javascript/pull/17318)) +- fix(node): Assign default export of `openai` to the instrumented fn ([#17320](https://github.com/getsentry/sentry-javascript/pull/17320)) +- fix(replay): Call `sendBufferedReplayOrFlush` when opening/sending feedback ([#17236](https://github.com/getsentry/sentry-javascript/pull/17236)) + +## 10.1.0 + +- feat(nuxt): Align build-time options to follow bundler plugins structure ([#17255](https://github.com/getsentry/sentry-javascript/pull/17255)) +- fix(browser-utils): Ensure web vital client hooks unsubscribe correctly ([#17272](https://github.com/getsentry/sentry-javascript/pull/17272)) +- fix(browser): Ensure request from `diagnoseSdkConnectivity` doesn't create span ([#17280](https://github.com/getsentry/sentry-javascript/pull/17280)) + +## 10.0.0 + +Version `10.0.0` marks a release of the Sentry JavaScript SDKs that contains breaking changes. The goal of this release is to primarily upgrade the underlying OpenTelemetry dependencies to v2 with minimal breaking changes. + +### How To Upgrade + +Please carefully read through the migration guide in the Sentry docs on how to upgrade from version 9 to version 10. Make sure to select your specific platform/framework in the top left corner: https://docs.sentry.io/platforms/javascript/migration/v9-to-v10/ + +A comprehensive migration guide outlining all changes can be found within the Sentry JavaScript SDK Repository: https://github.com/getsentry/sentry-javascript/blob/develop/MIGRATION.md + +### Breaking Changes + +- feat!: Bump to OpenTelemetry v2 ([#16872](https://github.com/getsentry/sentry-javascript/pull/16872)) +- feat(browser)!: Remove FID web vital collection ([#17076](https://github.com/getsentry/sentry-javascript/pull/17076)) +- feat(core)!: Remove `BaseClient` ([#17071](https://github.com/getsentry/sentry-javascript/pull/17071)) +- feat(core)!: Remove `enableLogs` and `beforeSendLog` experimental options ([#17063](https://github.com/getsentry/sentry-javascript/pull/17063)) +- feat(core)!: Remove `hasTracingEnabled` ([#17072](https://github.com/getsentry/sentry-javascript/pull/17072)) +- feat(core)!: Remove deprecated logger ([#17061](https://github.com/getsentry/sentry-javascript/pull/17061)) +- feat(replay)!: Promote `_experiments.autoFlushOnFeedback` option as default ([#17220](https://github.com/getsentry/sentry-javascript/pull/17220)) +- chore(deps)!: Bump bundler plugins to v4 ([#17089](https://github.com/getsentry/sentry-javascript/pull/17089)) + +### Other Changes + +- feat(astro): Implement Request Route Parametrization for Astro 5 ([#17105](https://github.com/getsentry/sentry-javascript/pull/17105)) +- feat(astro): Parametrize routes on client-side ([#17133](https://github.com/getsentry/sentry-javascript/pull/17133)) +- feat(aws): Add `SentryNodeServerlessSDKv10` v10 AWS Lambda Layer ([#17069](https://github.com/getsentry/sentry-javascript/pull/17069)) +- feat(aws): Create unified lambda layer for ESM and CJS ([#17012](https://github.com/getsentry/sentry-javascript/pull/17012)) +- feat(aws): Detect SDK source for AWS Lambda layer ([#17128](https://github.com/getsentry/sentry-javascript/pull/17128)) +- feat(core): Add missing openai tool calls attributes ([#17226](https://github.com/getsentry/sentry-javascript/pull/17226)) +- feat(core): Add shared `flushIfServerless` function ([#17177](https://github.com/getsentry/sentry-javascript/pull/17177)) +- feat(core): Implement `strictTraceContinuation` ([#16313](https://github.com/getsentry/sentry-javascript/pull/16313)) +- feat(core): MCP server instrumentation without breaking Miniflare ([#16817](https://github.com/getsentry/sentry-javascript/pull/16817)) +- feat(deps): bump @prisma/instrumentation from 6.11.1 to 6.12.0 ([#17117](https://github.com/getsentry/sentry-javascript/pull/17117)) +- feat(meta): Unify detection of serverless environments and add Cloud Run ([#17168](https://github.com/getsentry/sentry-javascript/pull/17168)) +- feat(nestjs): Switch to OTel core instrumentation ([#17068](https://github.com/getsentry/sentry-javascript/pull/17068)) +- feat(node-native): Upgrade `@sentry-internal/node-native-stacktrace` to `0.2.2` ([#17207](https://github.com/getsentry/sentry-javascript/pull/17207)) +- feat(node): Add `shouldHandleError` option to `fastifyIntegration` ([#16845](https://github.com/getsentry/sentry-javascript/pull/16845)) +- feat(node): Add firebase integration ([#16719](https://github.com/getsentry/sentry-javascript/pull/16719)) +- feat(node): Instrument stream responses for openai ([#17110](https://github.com/getsentry/sentry-javascript/pull/17110)) +- feat(react-router): Add `createSentryHandleError` ([#17235](https://github.com/getsentry/sentry-javascript/pull/17235)) +- feat(react-router): Automatically flush on serverless for loaders/actions ([#17234](https://github.com/getsentry/sentry-javascript/pull/17234)) +- feat(react-router): Automatically flush on Vercel for request handlers ([#17232](https://github.com/getsentry/sentry-javascript/pull/17232)) +- fix(astro): Construct parametrized route during runtime ([#17190](https://github.com/getsentry/sentry-javascript/pull/17190)) +- fix(aws): Add layer build output to nx cache ([#17148](https://github.com/getsentry/sentry-javascript/pull/17148)) +- fix(aws): Fix path to packages directory ([#17112](https://github.com/getsentry/sentry-javascript/pull/17112)) +- fix(aws): Resolve all Sentry packages to local versions in layer build ([#17106](https://github.com/getsentry/sentry-javascript/pull/17106)) +- fix(aws): Use file link in dependency version ([#17111](https://github.com/getsentry/sentry-javascript/pull/17111)) +- fix(cloudflare): Allow non uuid workflow instance IDs ([#17121](https://github.com/getsentry/sentry-javascript/pull/17121)) +- fix(cloudflare): Avoid turning DurableObject sync methods into async ([#17184](https://github.com/getsentry/sentry-javascript/pull/17184)) +- fix(core): Fix OpenAI SDK private field access by binding non-instrumented fns ([#17163](https://github.com/getsentry/sentry-javascript/pull/17163)) +- fix(core): Fix operation name for openai responses API ([#17206](https://github.com/getsentry/sentry-javascript/pull/17206)) +- fix(core): Update ai.response.object to gen_ai.response.object ([#17153](https://github.com/getsentry/sentry-javascript/pull/17153)) +- fix(nextjs): Flush in route handlers ([#17223](https://github.com/getsentry/sentry-javascript/pull/17223)) +- fix(nextjs): Handle async params in url extraction ([#17162](https://github.com/getsentry/sentry-javascript/pull/17162)) +- fix(nextjs): Update stackframe calls for next v15.5 ([#17156](https://github.com/getsentry/sentry-javascript/pull/17156)) +- fix(node): Add mechanism to `fastifyIntegration` error handler ([#17208](https://github.com/getsentry/sentry-javascript/pull/17208)) +- fix(node): Ensure tool errors for `vercelAiIntegration` have correct trace connected ([#17132](https://github.com/getsentry/sentry-javascript/pull/17132)) +- fix(node): Fix exports for openai instrumentation ([#17238](https://github.com/getsentry/sentry-javascript/pull/17238)) +- fix(node): Handle stack traces with data URI filenames ([#17218](https://github.com/getsentry/sentry-javascript/pull/17218)) +- fix(react): Memoize wrapped component to prevent rerenders ([#17230](https://github.com/getsentry/sentry-javascript/pull/17230)) +- fix(remix): Ensure source maps upload fails silently if Sentry CLI fails ([#17082](https://github.com/getsentry/sentry-javascript/pull/17082)) +- fix(replay): Fix re-sampled sessions after a click ([#17008](https://github.com/getsentry/sentry-javascript/pull/17008)) +- fix(svelte): Do not insert preprocess code in script module in Svelte 5 ([#17114](https://github.com/getsentry/sentry-javascript/pull/17114)) +- fix(sveltekit): Align error status filtering and mechanism in `handleErrorWithSentry` ([#17157](https://github.com/getsentry/sentry-javascript/pull/17157)) + +Work in this release was contributed by @richardjelinek-fastest. Thank you for your contribution! + +## 9.44.2 + +This release is publishing the AWS Lambda Layer under `SentryNodeServerlessSDKv9`. The previous release `9.44.1` accidentally published the layer under `SentryNodeServerlessSDKv10`. + +## 9.44.1 + +- fix(replay/v9): Call sendBufferedReplayOrFlush when opening/sending feedback ([#17270](https://github.com/getsentry/sentry-javascript/pull/17270)) + +## 9.44.0 + +- feat(replay/v9): Deprecate `_experiments.autoFlushOnFeedback` ([#17219](https://github.com/getsentry/sentry-javascript/pull/17219)) +- feat(v9/core): Add shared `flushIfServerless` function ([#17239](https://github.com/getsentry/sentry-javascript/pull/17239)) +- feat(v9/node-native): Upgrade `@sentry-internal/node-native-stacktrace` to `0.2.2` ([#17256](https://github.com/getsentry/sentry-javascript/pull/17256)) +- feat(v9/react-router): Add `createSentryHandleError` ([#17244](https://github.com/getsentry/sentry-javascript/pull/17244)) +- feat(v9/react-router): Automatically flush on serverless for loaders/actions ([#17243](https://github.com/getsentry/sentry-javascript/pull/17243)) +- feat(v9/react-router): Automatically flush on serverless for request handler ([#17242](https://github.com/getsentry/sentry-javascript/pull/17242)) +- fix(v9/astro): Construct parametrized route during runtime ([#17227](https://github.com/getsentry/sentry-javascript/pull/17227)) +- fix(v9/nextjs): Flush in route handlers ([#17245](https://github.com/getsentry/sentry-javascript/pull/17245)) +- fix(v9/node): Fix exports for openai instrumentation ([#17238](https://github.com/getsentry/sentry-javascript/pull/17238)) (#17241) + +## 9.43.0 + +- feat(v9/core): add MCP server instrumentation ([#17196](https://github.com/getsentry/sentry-javascript/pull/17196)) +- feat(v9/meta): Unify detection of serverless environments and add Cloud Run ([#17204](https://github.com/getsentry/sentry-javascript/pull/17204)) +- fix(v9/node): Add mechanism to `fastifyIntegration` error handler ([#17211](https://github.com/getsentry/sentry-javascript/pull/17211)) +- fix(v9/replay): Fix re-sampled sessions after a click ([#17195](https://github.com/getsentry/sentry-javascript/pull/17195)) + +## 9.42.1 + +- fix(v9/astro): Revert Astro v5 storing route data to `globalThis` ([#17185](https://github.com/getsentry/sentry-javascript/pull/17185)) +- fix(v9/cloudflare): Avoid turning DurableObject sync methods into async ([#17187](https://github.com/getsentry/sentry-javascript/pull/17187)) +- fix(v9/nextjs): Handle async params in url extraction ([#17176](https://github.com/getsentry/sentry-javascript/pull/17176)) +- fix(v9/sveltekit): Align error status filtering and mechanism in `handleErrorWithSentry` ([#17174](https://github.com/getsentry/sentry-javascript/pull/17174)) + +## 9.42.0 + +- feat(v9/aws): Detect SDK source for AWS Lambda layer ([#17150](https://github.com/getsentry/sentry-javascript/pull/17150)) +- fix(v9/core): Fix OpenAI SDK private field access by binding non-instrumented fns ([#17167](https://github.com/getsentry/sentry-javascript/pull/17167)) +- fix(v9/core): Update ai.response.object to gen_ai.response.object ([#17155](https://github.com/getsentry/sentry-javascript/pull/17155)) +- fix(v9/nextjs): Update stackframe calls for next v15.5 ([#17161](https://github.com/getsentry/sentry-javascript/pull/17161)) + +## 9.41.0 + +### Important Changes + +- **feat(v9/core): Deprecate experimental `enableLogs` and `beforeSendLog` option ([#17092](https://github.com/getsentry/sentry-javascript/pull/17092))** + +Sentry now has support for [structured logging](https://docs.sentry.io/product/explore/logs/getting-started/). Previously to enable structured logging, you had to use the `_experiments.enableLogs` and `_experiments.beforeSendLog` options. These options have been deprecated in favor of the top-level `enableLogs` and `beforeSendLog` options. + +```js +// before +Sentry.init({ + _experiments: { + enableLogs: true, + beforeSendLog: log => { + return log; + }, + }, +}); + +// after +Sentry.init({ + enableLogs: true, + beforeSendLog: log => { + return log; + }, +}); +``` + +- **feat(astro): Implement parameterized routes** + - feat(v9/astro): Parametrize dynamic server routes ([#17141](https://github.com/getsentry/sentry-javascript/pull/17141)) + - feat(v9/astro): Parametrize routes on client-side ([#17143](https://github.com/getsentry/sentry-javascript/pull/17143)) + +Server-side and client-side parameterized routes are now supported in the Astro SDK. No configuration changes are required. + +### Other Changes + +- feat(v9/node): Add shouldHandleError option to fastifyIntegration ([#17123](https://github.com/getsentry/sentry-javascript/pull/17123)) +- fix(v9/cloudflare) Allow non UUID workflow instance IDs ([#17135](https://github.com/getsentry/sentry-javascript/pull/17135)) +- fix(v9/node): Ensure tool errors for `vercelAiIntegration` have correct trace ([#17142](https://github.com/getsentry/sentry-javascript/pull/17142)) +- fix(v9/remix): Ensure source maps upload fails silently if Sentry CLI fails ([#17095](https://github.com/getsentry/sentry-javascript/pull/17095)) +- fix(v9/svelte): Do not insert preprocess code in script module in Svelte 5 ([#17124](https://github.com/getsentry/sentry-javascript/pull/17124)) + +Work in this release was contributed by @richardjelinek-fastest. Thank you for your contribution! + +## 9.40.0 + +### Important Changes + +- **feat(browser): Add debugId sync APIs between web worker and main thread ([#16981](https://github.com/getsentry/sentry-javascript/pull/16981))** + +This release adds two Browser SDK APIs to let the main thread know about debugIds of worker files: + +- `webWorkerIntegration({worker})` to be used in the main thread +- `registerWebWorker({self})` to be used in the web worker + +```js +// main.js +Sentry.init({...}) + +const worker = new MyWorker(...); + +Sentry.addIntegration(Sentry.webWorkerIntegration({ worker })); + +worker.addEventListener('message', e => {...}); +``` + +```js +// worker.js +Sentry.registerWebWorker({ self }); + +self.postMessage(...); +``` + +- **feat(core): Deprecate logger in favor of debug ([#17040](https://github.com/getsentry/sentry-javascript/pull/17040))** + +The internal SDK `logger` export from `@sentry/core` has been deprecated in favor of the `debug` export. `debug` only exposes `log`, `warn`, and `error` methods but is otherwise identical to `logger`. Note that this deprecation does not affect the `logger` export from other packages (like `@sentry/browser` or `@sentry/node`) which is used for Sentry Logging. + +```js +import { logger, debug } from '@sentry/core'; + +// before +logger.info('This is an info message'); + +// after +debug.log('This is an info message'); +``` + +- **feat(node): Add OpenAI integration ([#17022](https://github.com/getsentry/sentry-javascript/pull/17022))** + +This release adds official support for instrumenting OpenAI SDK calls in with Sentry tracing, following OpenTelemetry semantic conventions for Generative AI. It instruments: + +- `client.chat.completions.create()` - For chat-based completions +- `client.responses.create()` - For the responses API + +```js +// The integration respects your `sendDefaultPii` option, but you can override the behavior in the integration options + +Sentry.init({ + dsn: '__DSN__', + integrations: [ + Sentry.openAIIntegration({ + recordInputs: true, // Force recording prompts + recordOutputs: true, // Force recording responses + }), + ], +}); +``` + +### Other Changes + +- feat(node-core): Expand `@opentelemetry/instrumentation` range to cover `0.203.0` ([#17043](https://github.com/getsentry/sentry-javascript/pull/17043)) +- fix(cloudflare): Ensure errors get captured from durable objects ([#16838](https://github.com/getsentry/sentry-javascript/pull/16838)) +- fix(sveltekit): Ensure server errors from streamed responses are sent ([#17044](https://github.com/getsentry/sentry-javascript/pull/17044)) + +Work in this release was contributed by @0xbad0c0d3 and @tommy-gilligan. Thank you for your contributions! + +## 9.39.0 + +### Important Changes + +- **feat(browser): Add `afterStartPageloadSpan` hook to improve spanId assignment on web vital spans ([#16893](https://github.com/getsentry/sentry-javascript/pull/16893))** + +This PR adds a new afterStartPageloadSpan lifecycle hook to more robustly assign the correct pageload span ID to web vital spans, replacing the previous unreliable "wait for a tick" approach with a direct callback that fires when the pageload span becomes available. + +- **feat(nextjs): Client-side parameterized routes ([#16934](https://github.com/getsentry/sentry-javascript/pull/16934))** + +This PR implements client-side parameterized routes for Next.js by leveraging an injected manifest within the existing app-router instrumentation to automatically parameterize all client-side transactions (e.g. `users/123` and `users/456` now become become `users/:id`). + +- **feat(node): Drop 401-404 and 3xx status code spans by default ([#16972](https://github.com/getsentry/sentry-javascript/pull/16972))** + +This PR changes the default behavior in the Node SDK to drop HTTP spans with 401-404 and 3xx status codes by default to reduce noise in tracing data. + +### Other Changes + +- feat(core): Prepend vercel ai attributes with `vercel.ai.X` ([#16908](https://github.com/getsentry/sentry-javascript/pull/16908)) +- feat(nextjs): Add `disableSentryWebpackConfig` flag ([#17013](https://github.com/getsentry/sentry-javascript/pull/17013)) +- feat(nextjs): Build app manifest ([#16851](https://github.com/getsentry/sentry-javascript/pull/16851)) +- feat(nextjs): Inject manifest into client for turbopack builds ([#16902](https://github.com/getsentry/sentry-javascript/pull/16902)) +- feat(nextjs): Inject manifest into client for webpack builds ([#16857](https://github.com/getsentry/sentry-javascript/pull/16857)) +- feat(node-native): Add option to disable event loop blocked detection ([#16919](https://github.com/getsentry/sentry-javascript/pull/16919)) +- feat(react-router): Ensure http.server route handling is consistent ([#16986](https://github.com/getsentry/sentry-javascript/pull/16986)) +- fix(core): Avoid prolonging idle span when starting standalone span ([#16928](https://github.com/getsentry/sentry-javascript/pull/16928)) +- fix(core): Remove side-effect from `tracing/errors.ts` ([#16888](https://github.com/getsentry/sentry-javascript/pull/16888)) +- fix(core): Wrap `beforeSendLog` in `consoleSandbox` ([#16968](https://github.com/getsentry/sentry-javascript/pull/16968)) +- fix(node-core): Apply correct SDK metadata ([#17014](https://github.com/getsentry/sentry-javascript/pull/17014)) +- fix(react-router): Ensure that all browser spans have `source=route` ([#16984](https://github.com/getsentry/sentry-javascript/pull/16984)) + +Work in this release was contributed by @janpapenbrock. Thank you for your contribution! + +## 9.38.0 + +### Important Changes + +- **chore: Add craft entry for @sentry/node-native ([#16907](https://github.com/getsentry/sentry-javascript/pull/16907))** + +This release publishes the `@sentry/node-native` SDK. + +### Other Changes + +- feat(core): Introduce `debug` to replace `logger` ([#16906](https://github.com/getsentry/sentry-javascript/pull/16906)) +- fix(browser): Guard `nextHopProtocol` when adding resource spans ([#16900](https://github.com/getsentry/sentry-javascript/pull/16900)) + +## 9.37.0 + +### Important Changes + +- **feat(nuxt): Parametrize SSR routes ([#16843](https://github.com/getsentry/sentry-javascript/pull/16843))** + + When requesting dynamic or catch-all routes in Nuxt, those will now be shown as parameterized routes in Sentry. + For example, `/users/123` will be shown as `/users/:userId()` in Sentry. This will make it easier to identify patterns and make grouping easier. + +### Other Changes + +- feat(astro): Deprecate passing runtime config to astro integration ([#16839](https://github.com/getsentry/sentry-javascript/pull/16839)) +- feat(browser): Add `beforeStartNavigationSpan` lifecycle hook ([#16863](https://github.com/getsentry/sentry-javascript/pull/16863)) +- feat(browser): Detect redirects when emitting navigation spans ([#16324](https://github.com/getsentry/sentry-javascript/pull/16324)) +- feat(cloudflare): Add option to opt out of capturing errors in `wrapRequestHandler` ([#16852](https://github.com/getsentry/sentry-javascript/pull/16852)) +- feat(feedback): Return the eventId into the onSubmitSuccess callback ([#16835](https://github.com/getsentry/sentry-javascript/pull/16835)) +- feat(vercel-edge): Do not vendor in all OpenTelemetry dependencies ([#16841](https://github.com/getsentry/sentry-javascript/pull/16841)) +- fix(browser): Ensure standalone CLS and LCP spans have traceId of pageload span ([#16864](https://github.com/getsentry/sentry-javascript/pull/16864)) +- fix(nextjs): Use value injection loader on `instrumentation-client.ts|js` ([#16855](https://github.com/getsentry/sentry-javascript/pull/16855)) +- fix(sveltekit): Avoid capturing `redirect()` calls as errors in Cloudflare ([#16853](https://github.com/getsentry/sentry-javascript/pull/16853)) +- docs(nextjs): Update `deleteSourcemapsAfterUpload` jsdoc default value ([#16867](https://github.com/getsentry/sentry-javascript/pull/16867)) + +Work in this release was contributed by @zachkirsch. Thank you for your contribution! + +## 9.36.0 + +### Important Changes + +- **feat(node-core): Add node-core SDK ([#16745](https://github.com/getsentry/sentry-javascript/pull/16745))** + +This release adds a new SDK `@sentry/node-core` which ships without any OpenTelemetry instrumententation out of the box. All OpenTelemetry dependencies are peer dependencies and OpenTelemetry has to be set up manually. + +Use `@sentry/node-core` when: + +- You already have OpenTelemetry set up +- You need custom OpenTelemetry configuration +- You want minimal dependencies +- You need fine-grained control over instrumentation + +Use `@sentry/node` when: + +- You want an automatic setup +- You're new to OpenTelemetry +- You want sensible defaults +- You prefer convenience over control + +* **feat(node): Deprecate ANR integration ([#16832](https://github.com/getsentry/sentry-javascript/pull/16832))** + +The ANR integration has been deprecated and will be removed in future versions. Use `eventLoopBlockIntegration` from `@sentry/node-native` instead. + +- **feat(replay): Add `_experiments.ignoreMutations` option ([#16816](https://github.com/getsentry/sentry-javascript/pull/16816))** + +This replay option allows to configure a selector list of elements to not capture mutations for. + +```js +Sentry.replayIntegration({ + _experiments: { + ignoreMutations: ['.dragging'], + }, +}); +``` + +### Other changes + +- feat(deps): bump @prisma/instrumentation from 6.10.1 to 6.11.1 ([#16833](https://github.com/getsentry/sentry-javascript/pull/16833)) +- feat(nextjs): Add flag for suppressing router transition warning ([#16823](https://github.com/getsentry/sentry-javascript/pull/16823)) +- feat(nextjs): Automatically skip middleware requests for tunnel route ([#16812](https://github.com/getsentry/sentry-javascript/pull/16812)) +- feat(replay): Export compression worker from `@sentry/replay-internal` ([#16794](https://github.com/getsentry/sentry-javascript/pull/16794)) +- fix(browser): Avoid 4xx response for succesful `diagnoseSdkConnectivity` request ([#16840](https://github.com/getsentry/sentry-javascript/pull/16840)) +- fix(browser): Guard against undefined nextHopProtocol ([#16806](https://github.com/getsentry/sentry-javascript/pull/16806)) +- fix(cloudflare): calculate retries not attempts ([#16834](https://github.com/getsentry/sentry-javascript/pull/16834)) +- fix(nuxt): Parametrize routes on the server-side ([#16785](https://github.com/getsentry/sentry-javascript/pull/16785)) +- fix(vue): Make pageload span handling more reliable ([#16799](https://github.com/getsentry/sentry-javascript/pull/16799)) + +Work in this release was contributed by @Spice-King and @stayallive. Thank you for your contributions! + +## 9.35.0 + +- feat(browser): Add ElementTiming instrumentation and spans ([#16589](https://github.com/getsentry/sentry-javascript/pull/16589)) +- feat(browser): Export `Context` and `Contexts` types ([#16763](https://github.com/getsentry/sentry-javascript/pull/16763)) +- feat(cloudflare): Add user agent to cloudflare spans ([#16793](https://github.com/getsentry/sentry-javascript/pull/16793)) +- feat(node): Add `eventLoopBlockIntegration` ([#16709](https://github.com/getsentry/sentry-javascript/pull/16709)) +- feat(node): Export server-side feature flag integration shims ([#16735](https://github.com/getsentry/sentry-javascript/pull/16735)) +- feat(node): Update vercel ai integration attributes ([#16721](https://github.com/getsentry/sentry-javascript/pull/16721)) +- fix(astro): Handle errors in middlewares better ([#16693](https://github.com/getsentry/sentry-javascript/pull/16693)) +- fix(browser): Ensure explicit `parentSpan` is considered ([#16776](https://github.com/getsentry/sentry-javascript/pull/16776)) +- fix(node): Avoid using dynamic `require` for fastify integration ([#16789](https://github.com/getsentry/sentry-javascript/pull/16789)) +- fix(nuxt): Add `@sentry/cloudflare` as optional peerDependency ([#16782](https://github.com/getsentry/sentry-javascript/pull/16782)) +- fix(nuxt): Ensure order of plugins is consistent ([#16798](https://github.com/getsentry/sentry-javascript/pull/16798)) +- fix(nestjs): Fix exception handling in `@Cron` decorated tasks ([#16792](https://github.com/getsentry/sentry-javascript/pull/16792)) + +Work in this release was contributed by @0xbad0c0d3 and @alSergey. Thank you for your contributions! + +## 9.34.0 + +### Important Changes + +- **feat(nuxt): Add Cloudflare Nitro plugin ([#15597](https://github.com/getsentry/sentry-javascript/pull/15597))** + + A Nitro plugin for `@sentry/nuxt` which initializes Sentry when deployed to Cloudflare (`cloudflare-pages` preset). + 1. Remove the previous server config file: `sentry.server.config.ts` + 2. Add a plugin in `server/plugins` (e.g. `server/plugins/sentry-cloudflare-setup.ts`) + 3. Add this code in your plugin file + + ```javascript + // server/plugins/sentry-cloudflare-setup.ts (filename does not matter) + import { sentryCloudflareNitroPlugin } from '@sentry/nuxt/module/plugins'; + + export default defineNitroPlugin( + sentryCloudflareNitroPlugin({ + dsn: 'https://dsn', + tracesSampleRate: 1.0, + }), + ); + ``` + + or with access to `nitroApp`: + + ```javascript + // server/plugins/sentry-cloudflare-setup.ts (filename does not matter) + import { sentryCloudflareNitroPlugin } from '@sentry/nuxt/module/plugins'; + + export default defineNitroPlugin(sentryCloudflareNitroPlugin((nitroApp: NitroApp) => { + // You can access nitroApp here if needed + return ({ + dsn: 'https://dsn', + tracesSampleRate: 1.0, + }) + })) + ``` + +### Other Changes + +- feat(browser): Record standalone LCP spans ([#16591](https://github.com/getsentry/sentry-javascript/pull/16591)) +- fix(nuxt): Only add OTel alias in dev mode ([#16756](https://github.com/getsentry/sentry-javascript/pull/16756)) + +## 9.33.0 + +### Important Changes + +- **feat: Add opt-in `vercelAiIntegration` to cloudflare & vercel-edge ([#16732](https://github.com/getsentry/sentry-javascript/pull/16732))** + +The `vercelAiIntegration` is now available as opt-in for the Cloudflare and the Next.js SDK for Vercel Edge. +To use it, add the integration in `Sentry.init` + +```js +Sentry.init({ + tracesSampleRate: 1.0, + integrations: [Sentry.vercelAIIntegration()], +}); +``` + +And enable telemetry for Vercel AI calls + +```js +const result = await generateText({ + model: openai('gpt-4o'), + experimental_telemetry: { + isEnabled: true, + }, +}); +``` + +- **feat(node): Add postgresjs instrumentation ([#16665](https://github.com/getsentry/sentry-javascript/pull/16665))** + +The Node.js SDK now includes instrumentation for [Postgres.js](https://www.npmjs.com/package/postgres). + +- **feat(node): Use diagnostics channel for Fastify v5 error handling ([#16715](https://github.com/getsentry/sentry-javascript/pull/16715))** + +If you're on Fastify v5, you no longer need to call `setupFastifyErrorHandler`. It is done automatically by the node SDK. Older versions still rely on calling `setupFastifyErrorHandler`. + +### Other Changes + +- feat(cloudflare): Allow interop with OpenTelemetry emitted spans ([#16714](https://github.com/getsentry/sentry-javascript/pull/16714)) +- feat(cloudflare): Flush after `waitUntil` ([#16681](https://github.com/getsentry/sentry-javascript/pull/16681)) +- fix(nextjs): Remove `ai` from default server external packages ([#16736](https://github.com/getsentry/sentry-javascript/pull/16736)) + +Work in this release was contributed by @0xbad0c0d3. Thank you for your contribution! + +## 9.32.0 + +### Important Changes + +- feat(browser): Add CLS sources to span attributes ([#16710](https://github.com/getsentry/sentry-javascript/pull/16710)) + +Enhances CLS (Cumulative Layout Shift) spans by adding attributes detailing the elements that caused layout shifts. + +- feat(cloudflare): Add `instrumentWorkflowWithSentry` to instrument workflows ([#16672](https://github.com/getsentry/sentry-javascript/pull/16672)) + +We've added support for Cloudflare Workflows, enabling comprehensive tracing for your workflow runs. This integration uses the workflow's instanceId as the Sentry trace_id and for sampling, linking all steps together. You'll now be able to see full traces, including retries with exponential backoff. + +- feat(pino-transport): Add functionality to send logs to sentry ([#16667](https://github.com/getsentry/sentry-javascript/pull/16667)) + +Adds the ability to send logs to Sentry via a pino transport. + +### Other Changes + +- feat(nextjs): Expose top level buildTime `errorHandler` option ([#16718](https://github.com/getsentry/sentry-javascript/pull/16718)) +- feat(node): update pipeline spans to use agent naming ([#16712](https://github.com/getsentry/sentry-javascript/pull/16712)) +- feat(deps): bump @prisma/instrumentation from 6.9.0 to 6.10.1 ([#16698](https://github.com/getsentry/sentry-javascript/pull/16698)) +- fix(sveltekit): Export logger from sveltekit worker ([#16716](https://github.com/getsentry/sentry-javascript/pull/16716)) +- fix(google-cloud-serverless): Make `CloudEventsContext` compatible with `CloudEvent` ([#16705](https://github.com/getsentry/sentry-javascript/pull/16705)) +- fix(nextjs): Stop injecting release value when create release options is set to `false` ([#16695](https://github.com/getsentry/sentry-javascript/pull/16695)) +- fix(node): account for Object. syntax with local variables matching ([#16702](https://github.com/getsentry/sentry-javascript/pull/16702)) +- fix(nuxt): Add alias for `@opentelemetry/resources` ([#16727](https://github.com/getsentry/sentry-javascript/pull/16727)) + +Work in this release was contributed by @flaeppe. Thank you for your contribution! + +## 9.31.0 + +### Important Changes + +- feat(nextjs): Add option for auto-generated random tunnel route ([#16626](https://github.com/getsentry/sentry-javascript/pull/16626)) + +Adds an option to automatically generate a random tunnel route for the Next.js SDK. This helps prevent ad blockers and other tools from blocking Sentry requests by using a randomized path instead of the predictable `/monitoring` endpoint. + +- feat(core): Allow to pass `scope` & `client` to `getTraceData` ([#16633](https://github.com/getsentry/sentry-javascript/pull/16633)) + +Adds the ability to pass custom `scope` and `client` parameters to the `getTraceData` function, providing more flexibility when generating trace data for distributed tracing. + +### Other Changes + +- feat(core): Add support for `x-forwarded-host` and `x-forwarded-proto` headers ([#16687](https://github.com/getsentry/sentry-javascript/pull/16687)) +- deps: Remove unused `@sentry/opentelemetry` dependency ([#16677](https://github.com/getsentry/sentry-javascript/pull/16677)) +- deps: Update all bundler plugin instances to latest & allow caret ranges ([#16641](https://github.com/getsentry/sentry-javascript/pull/16641)) +- feat(deps): Bump @prisma/instrumentation from 6.8.2 to 6.9.0 ([#16608](https://github.com/getsentry/sentry-javascript/pull/16608)) +- feat(flags): add node support for generic featureFlagsIntegration and move utils to core ([#16585](https://github.com/getsentry/sentry-javascript/pull/16585)) +- feat(flags): capture feature flag evaluations on spans ([#16485](https://github.com/getsentry/sentry-javascript/pull/16485)) +- feat(pino): Add initial package for `@sentry/pino-transport` ([#16652](https://github.com/getsentry/sentry-javascript/pull/16652)) +- fix: Wait for the correct clientWidth/clientHeight when showing Feedback Screenshot previews ([#16648](https://github.com/getsentry/sentry-javascript/pull/16648)) +- fix(browser): Remove usage of Array.at() method ([#16647](https://github.com/getsentry/sentry-javascript/pull/16647)) +- fix(core): Improve `safeJoin` usage in console logging integration ([#16658](https://github.com/getsentry/sentry-javascript/pull/16658)) +- fix(google-cloud-serverless): Make `CloudEvent` type compatible ([#16661](https://github.com/getsentry/sentry-javascript/pull/16661)) +- fix(nextjs): Fix lookup of `instrumentation-client.js` file ([#16637](https://github.com/getsentry/sentry-javascript/pull/16637)) +- fix(node): Ensure graphql errors result in errored spans ([#16678](https://github.com/getsentry/sentry-javascript/pull/16678)) + +## 9.30.0 + +- feat(nextjs): Add URL to tags of server components and generation functions issues ([#16500](https://github.com/getsentry/sentry-javascript/pull/16500)) +- feat(nextjs): Ensure all packages we auto-instrument are externalized ([#16552](https://github.com/getsentry/sentry-javascript/pull/16552)) +- feat(node): Automatically enable `vercelAiIntegration` when `ai` module is detected ([#16565](https://github.com/getsentry/sentry-javascript/pull/16565)) +- feat(node): Ensure `modulesIntegration` works in more environments ([#16566](https://github.com/getsentry/sentry-javascript/pull/16566)) +- feat(core): Don't gate user on logs with `sendDefaultPii` ([#16527](https://github.com/getsentry/sentry-javascript/pull/16527)) +- feat(browser): Add detail to measure spans and add regression tests ([#16557](https://github.com/getsentry/sentry-javascript/pull/16557)) +- feat(node): Update Vercel AI span attributes ([#16580](https://github.com/getsentry/sentry-javascript/pull/16580)) +- fix(opentelemetry): Ensure only orphaned spans of sent spans are sent ([#16590](https://github.com/getsentry/sentry-javascript/pull/16590)) + +## 9.29.0 + +### Important Changes + +- **feat(browser): Update `web-vitals` to 5.0.2 ([#16492](https://github.com/getsentry/sentry-javascript/pull/16492))** + +This release upgrades the `web-vitals` library to version 5.0.2. This upgrade could slightly change the collected web vital values and potentially also influence alerts and performance scores in the Sentry UI. + +### Other Changes + +- feat(deps): Bump @sentry/rollup-plugin from 3.4.0 to 3.5.0 ([#16524](https://github.com/getsentry/sentry-javascript/pull/16524)) +- feat(ember): Stop warning for `onError` usage ([#16547](https://github.com/getsentry/sentry-javascript/pull/16547)) +- feat(node): Allow to force activate `vercelAiIntegration` ([#16551](https://github.com/getsentry/sentry-javascript/pull/16551)) +- feat(node): Introduce `ignoreLayersType` option to koa integration ([#16553](https://github.com/getsentry/sentry-javascript/pull/16553)) +- fix(browser): Ensure `suppressTracing` does not leak when async ([#16545](https://github.com/getsentry/sentry-javascript/pull/16545)) +- fix(vue): Ensure root component render span always ends ([#16488](https://github.com/getsentry/sentry-javascript/pull/16488)) + +## 9.28.1 + +- feat(deps): Bump @sentry/cli from 2.45.0 to 2.46.0 ([#16516](https://github.com/getsentry/sentry-javascript/pull/16516)) +- fix(nextjs): Avoid tracing calls to symbolication server on dev ([#16533](https://github.com/getsentry/sentry-javascript/pull/16533)) +- fix(sveltekit): Add import attribute for node exports ([#16528](https://github.com/getsentry/sentry-javascript/pull/16528)) + +Work in this release was contributed by @eltigerchino. Thank you for your contribution! + +## 9.28.0 + +### Important Changes + +- **feat(nestjs): Stop creating spans for `TracingInterceptor` ([#16501](https://github.com/getsentry/sentry-javascript/pull/16501))** + +With this change we stop creating spans for `TracingInterceptor` as this interceptor only serves as an internal helper and adds noise for the user. + +- **feat(node): Update vercel ai spans as per new conventions ([#16497](https://github.com/getsentry/sentry-javascript/pull/16497))** + +This feature ships updates to the span names and ops to better match OpenTelemetry. This should make them more easily accessible to the new agents module view we are building. + +### Other Changes + +- fix(sveltekit): Export `vercelAIIntegration` from `@sentry/node` ([#16496](https://github.com/getsentry/sentry-javascript/pull/16496)) + +Work in this release was contributed by @agrattan0820. Thank you for your contribution! + +## 9.27.0 + +- feat(node): Expand how vercel ai input/outputs can be set ([#16455](https://github.com/getsentry/sentry-javascript/pull/16455)) +- feat(node): Switch to new semantic conventions for Vercel AI ([#16476](https://github.com/getsentry/sentry-javascript/pull/16476)) +- feat(react-router): Add component annotation plugin ([#16472](https://github.com/getsentry/sentry-javascript/pull/16472)) +- feat(react-router): Export wrappers for server loaders and actions ([#16481](https://github.com/getsentry/sentry-javascript/pull/16481)) +- fix(browser): Ignore unrealistically long INP values ([#16484](https://github.com/getsentry/sentry-javascript/pull/16484)) +- fix(react-router): Conditionally add `ReactRouterServer` integration ([#16470](https://github.com/getsentry/sentry-javascript/pull/16470)) + +## 9.26.0 + +- feat(react-router): Re-export functions from `@sentry/react` ([#16465](https://github.com/getsentry/sentry-javascript/pull/16465)) +- fix(nextjs): Skip re instrumentating on generate phase of experimental build mode ([#16410](https://github.com/getsentry/sentry-javascript/pull/16410)) +- fix(node): Ensure adding sentry-trace and baggage headers via SentryHttpInstrumentation doesn't crash ([#16473](https://github.com/getsentry/sentry-javascript/pull/16473)) + +## 9.25.1 + +- fix(otel): Don't ignore child spans after the root is sent ([#16416](https://github.com/getsentry/sentry-javascript/pull/16416)) + +## 9.25.0 + +### Important Changes + +- **feat(browser): Add option to ignore `mark` and `measure` spans ([#16443](https://github.com/getsentry/sentry-javascript/pull/16443))** + +This release adds an option to `browserTracingIntegration` that lets you ignore +`mark` and `measure` spans created from the `performance.mark(...)` and `performance.measure(...)` browser APIs: + +```js +Sentry.init({ + integrations: [ + Sentry.browserTracingIntegration({ + ignorePerformanceApiSpans: ['measure-to-ignore', /mark-to-ignore/], + }), + ], +}); +``` + +### Other Changes + +- feat(browser): Export getTraceData from the browser sdks ([#16433](https://github.com/getsentry/sentry-javascript/pull/16433)) +- feat(node): Add `includeServerName` option ([#16442](https://github.com/getsentry/sentry-javascript/pull/16442)) +- fix(nuxt): Remove setting `@sentry/nuxt` external ([#16444](https://github.com/getsentry/sentry-javascript/pull/16444)) + +## 9.24.0 + +### Important Changes + +- feat(angular): Bump `@sentry/angular` peer dependencies to add Angular 20 support ([#16414](https://github.com/getsentry/sentry-javascript/pull/16414)) + +This release adds support for Angular 20 to the Sentry Angular SDK `@sentry/angular`. + +### Other Changes + +- feat(browser): Add `unregisterOriginalCallbacks` option to `browserApiErrorsIntegration` ([#16412](https://github.com/getsentry/sentry-javascript/pull/16412)) +- feat(core): Add user to logs ([#16399](https://github.com/getsentry/sentry-javascript/pull/16399)) +- feat(core): Make sure Supabase db query insights are populated ([#16169](https://github.com/getsentry/sentry-javascript/pull/16169)) + +## 9.23.0 + +### Important changes + +- **feat(browser): option to ignore certain resource types ([#16389](https://github.com/getsentry/sentry-javascript/pull/16389))** + +Adds an option to opt out of certain `resource.*` spans via `ignoreResourceSpans`. + +For example, to opt out of `resource.script` spans: + +```js +Sentry.browserTracingIntegration({ + ignoreResourceSpans: ['resource.script'], +}), +``` + +### Other changes + +- feat: Export `isEnabled` from all SDKs ([#16405](https://github.com/getsentry/sentry-javascript/pull/16405)) +- feat(browser): Disable client when browser extension is detected in `init()` ([#16354](https://github.com/getsentry/sentry-javascript/pull/16354)) +- feat(core): Allow re-use of `captureLog` ([#16352](https://github.com/getsentry/sentry-javascript/pull/16352)) +- feat(core): Export `_INTERNAL_captureSerializedLog` ([#16387](https://github.com/getsentry/sentry-javascript/pull/16387)) +- feat(deps): bump @opentelemetry/semantic-conventions from 1.32.0 to 1.34.0 ([#16393](https://github.com/getsentry/sentry-javascript/pull/16393)) +- feat(deps): bump @prisma/instrumentation from 6.7.0 to 6.8.2 ([#16392](https://github.com/getsentry/sentry-javascript/pull/16392)) +- feat(deps): bump @sentry/cli from 2.43.0 to 2.45.0 ([#16395](https://github.com/getsentry/sentry-javascript/pull/16395)) +- feat(deps): bump @sentry/webpack-plugin from 3.3.1 to 3.5.0 ([#16394](https://github.com/getsentry/sentry-javascript/pull/16394)) +- feat(nextjs): Include `static/chunks/main-*` files for `widenClientFileUpload` ([#16406](https://github.com/getsentry/sentry-javascript/pull/16406)) +- feat(node): Do not add HTTP & fetch span instrumentation if tracing is disabled ([#15730](https://github.com/getsentry/sentry-javascript/pull/15730)) +- feat(nuxt): Added support for nuxt layers ([#16372](https://github.com/getsentry/sentry-javascript/pull/16372)) +- fix(browser): Ensure logs are flushed when sendClientReports=false ([#16351](https://github.com/getsentry/sentry-javascript/pull/16351)) +- fix(browser): Move `browserTracingIntegration` code to `setup` hook ([#16386](https://github.com/getsentry/sentry-javascript/pull/16386)) +- fix(cloudflare): Capture exceptions thrown in hono ([#16355](https://github.com/getsentry/sentry-javascript/pull/16355)) +- fix(node): Don't warn about Spotlight on empty NODE_ENV ([#16381](https://github.com/getsentry/sentry-javascript/pull/16381)) +- fix(node): Suppress Spotlight calls ([#16380](https://github.com/getsentry/sentry-javascript/pull/16380)) +- fix(nuxt): Add `@sentry/nuxt` as external in Rollup ([#16407](https://github.com/getsentry/sentry-javascript/pull/16407)) +- fix(opentelemetry): Ensure `withScope` keeps span active & `_getTraceInfoFromScope` works ([#16385](https://github.com/getsentry/sentry-javascript/pull/16385)) + +Work in this release was contributed by @Xenossolitarius. Thank you for your contribution! + +## 9.22.0 + +### Important changes + +- **Revert "feat(browser): Track measure detail as span attributes" ([#16348](https://github.com/getsentry/sentry-javascript/pull/16348))** + +This is a revert of a feature introduced in `9.20.0` with [#16240](https://github.com/getsentry/sentry-javascript/pull/16240). This feature was causing crashes in firefox, so we are reverting it. We will re-enable this functionality in the future after fixing the crash. + +### Other changes + +- feat(deps): bump @sentry/rollup-plugin from 3.1.2 to 3.2.1 ([#15511](https://github.com/getsentry/sentry-javascript/pull/15511)) +- fix(remix): Use generic types for `ServerBuild` argument and return ([#16336](https://github.com/getsentry/sentry-javascript/pull/16336)) + +## 9.21.0 + +- docs: Fix v7 migration link ([#14629](https://github.com/getsentry/sentry-javascript/pull/14629)) +- feat(node): Vendor in `@fastify/otel` ([#16328](https://github.com/getsentry/sentry-javascript/pull/16328)) +- fix(nestjs): Handle multiple `OnEvent` decorators ([#16306](https://github.com/getsentry/sentry-javascript/pull/16306)) +- fix(node): Avoid creating breadcrumbs for suppressed requests ([#16285](https://github.com/getsentry/sentry-javascript/pull/16285)) +- fix(remix): Add missing `client` exports to `server` and `cloudflare` entries ([#16341](https://github.com/getsentry/sentry-javascript/pull/16341)) + +Work in this release was contributed by @phthhieu. Thank you for your contribution! + +## 9.20.0 + +### Important changes + +- **feat(browser): Track measure detail as span attributes ([#16240](https://github.com/getsentry/sentry-javascript/pull/16240))** + +The SDK now automatically collects details passed to `performance.measure` options. + +### Other changes + +- feat(node): Add `maxIncomingRequestBodySize` ([#16225](https://github.com/getsentry/sentry-javascript/pull/16225)) +- feat(react-router): Add server action instrumentation ([#16292](https://github.com/getsentry/sentry-javascript/pull/16292)) +- feat(react-router): Filter manifest requests ([#16294](https://github.com/getsentry/sentry-javascript/pull/16294)) +- feat(replay): Extend default list for masking with `aria-label` ([#16192](https://github.com/getsentry/sentry-javascript/pull/16192)) +- fix(browser): Ensure pageload & navigation spans have correct data ([#16279](https://github.com/getsentry/sentry-javascript/pull/16279)) +- fix(cloudflare): Account for static fields in wrapper type ([#16303](https://github.com/getsentry/sentry-javascript/pull/16303)) +- fix(nextjs): Preserve `next.route` attribute on root spans ([#16297](https://github.com/getsentry/sentry-javascript/pull/16297)) +- feat(node): Fork isolation scope in tRPC middleware ([#16296](https://github.com/getsentry/sentry-javascript/pull/16296)) +- feat(core): Add `orgId` option to `init` and DSC (`sentry-org_id` in baggage) ([#16305](https://github.com/getsentry/sentry-javascript/pull/16305)) + +## 9.19.0 + +- feat(react-router): Add otel instrumentation for server requests ([#16147](https://github.com/getsentry/sentry-javascript/pull/16147)) +- feat(remix): Vendor in `opentelemetry-instrumentation-remix` ([#16145](https://github.com/getsentry/sentry-javascript/pull/16145)) +- fix(browser): Ensure spans auto-ended for navigations have `cancelled` reason ([#16277](https://github.com/getsentry/sentry-javascript/pull/16277)) +- fix(node): Pin `@fastify/otel` fork to direct url to allow installing without git ([#16287](https://github.com/getsentry/sentry-javascript/pull/16287)) +- fix(react): Handle nested parameterized routes in reactrouterv3 transaction normalization ([#16274](https://github.com/getsentry/sentry-javascript/pull/16274)) + +Work in this release was contributed by @sidx1024. Thank you for your contribution! + +## 9.18.0 + +### Important changes + +- **feat: Support Node 24 ([#16236](https://github.com/getsentry/sentry-javascript/pull/16236))** + +We now also publish profiling binaries for Node 24. + +### Other changes + +- deps(node): Bump `import-in-the-middle` to `1.13.1` ([#16260](https://github.com/getsentry/sentry-javascript/pull/16260)) +- feat: Export `consoleLoggingIntegration` from vercel edge sdk ([#16228](https://github.com/getsentry/sentry-javascript/pull/16228)) +- feat(cloudflare): Add support for email, queue, and tail handler ([#16233](https://github.com/getsentry/sentry-javascript/pull/16233)) +- feat(cloudflare): Improve http span data ([#16232](https://github.com/getsentry/sentry-javascript/pull/16232)) +- feat(nextjs): Add more attributes for generation functions ([#16214](https://github.com/getsentry/sentry-javascript/pull/16214)) +- feat(opentelemetry): Widen peer dependencies to support Otel v2 ([#16246](https://github.com/getsentry/sentry-javascript/pull/16246)) +- fix(core): Gracefully handle invalid baggage entries ([#16257](https://github.com/getsentry/sentry-javascript/pull/16257)) +- fix(node): Ensure traces are propagated without spans in Node 22+ ([#16221](https://github.com/getsentry/sentry-javascript/pull/16221)) +- fix(node): Use sentry forked `@fastify/otel` dependency with pinned Otel v1 deps ([#16256](https://github.com/getsentry/sentry-javascript/pull/16256)) +- fix(remix): Remove vendored types ([#16218](https://github.com/getsentry/sentry-javascript/pull/16218)) + +## 9.17.0 + +- feat(node): Migrate to `@fastify/otel` ([#15542](https://github.com/getsentry/sentry-javascript/pull/15542)) + +## 9.16.1 + +- fix(core): Make sure logs get flushed in server-runtime-client ([#16222](https://github.com/getsentry/sentry-javascript/pull/16222)) +- ref(node): Remove vercel flushing code that does nothing ([#16217](https://github.com/getsentry/sentry-javascript/pull/16217)) + +## 9.16.0 + +### Important changes + +- **feat: Create a Vite plugin that injects sentryConfig into the global config ([#16197](https://github.com/getsentry/sentry-javascript/pull/16197))** + +Add a new plugin `makeConfigInjectorPlugin` within our existing vite plugin that updates the global vite config with sentry options + +- **feat(browser): Add option to sample linked traces consistently ([#16037](https://github.com/getsentry/sentry-javascript/pull/16037))** + +This PR implements consistent sampling across traces as outlined in ([#15754](https://github.com/getsentry/sentry-javascript/pull/15754)) + +- **feat(cloudflare): Add support for durable objects ([#16180](https://github.com/getsentry/sentry-javascript/pull/16180))** + +This PR introduces a new `instrumentDurableObjectWithSentry` method to the SDK, which instruments durable objects. We capture both traces and errors automatically. + +- **feat(node): Add Prisma integration by default ([#16073](https://github.com/getsentry/sentry-javascript/pull/16073))** + +[Prisma integration](https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/integrations/prisma/) is enabled by default, it should work for both ESM and CJS. + +- **feat(react-router): Add client-side router instrumentation ([#16185](https://github.com/getsentry/sentry-javascript/pull/16185))** + +Adds client-side instrumentation for react router's `HydratedRouter`. To enable it, simply replace `browserTracingIntegration()` with `reactRouterTracingIntegration()` in your client-side init call. + +- **fix(node): Avoid double-wrapping http module ([#16177](https://github.com/getsentry/sentry-javascript/pull/16177))** + +When running your application in ESM mode, there have been scenarios that resulted in the `http`/`https` emitting duplicate spans for incoming requests. This was apparently caused by us double-wrapping the modules for incoming request isolation. + +In order to solve this problem, the modules are no longer monkey patched by us for request isolation. Instead, we register diagnostics*channel hooks to handle request isolation now. +While this is generally not expected to break anything, there is one tiny change that \_may* affect you if you have been relying on very specific functionality: + +The `ignoreOutgoingRequests` option of `httpIntegration` receives the `RequestOptions` as second argument. This type is not changed, however due to how the wrapping now works, we no longer pass through the full RequestOptions, but re-construct this partially based on the generated request. For the vast majority of cases, this should be fine, but for the sake of completeness, these are the only fields that may be available there going forward - other fields that _may_ have existed before may no longer be set: + +```ts +ignoreOutgoingRequests(url: string, { + method: string; + protocol: string; + host: string; + hostname: string; // same as host + path: string; + headers: OutgoingHttpHeaders; +}) +``` + +### Other changes + +- feat(cloudflare): Add logs exports ([#16165](https://github.com/getsentry/sentry-javascript/pull/16165)) +- feat(vercel-edge): Add logs export ([#16166](https://github.com/getsentry/sentry-javascript/pull/16166)) +- feat(cloudflare): Read `SENTRY_RELEASE` from `env` ([#16201](https://github.com/getsentry/sentry-javascript/pull/16201)) +- feat(node): Drop `http.server` spans with 404 status by default ([#16205](https://github.com/getsentry/sentry-javascript/pull/16205)) +- fix(browser): Respect manually set sentry tracing headers in XHR requests ([#16184](https://github.com/getsentry/sentry-javascript/pull/16184)) +- fix(core): Respect manually set sentry tracing headers in fetch calls ([#16183](https://github.com/getsentry/sentry-javascript/pull/16183)) +- fix(feedback): Prevent `removeFromDom()` from throwing ([#16030](https://github.com/getsentry/sentry-javascript/pull/16030)) +- fix(node): Use class constructor in docstring for winston transport ([#16167](https://github.com/getsentry/sentry-javascript/pull/16167)) +- fix(node): Fix vercel flushing logic & add test for it ([#16208](https://github.com/getsentry/sentry-javascript/pull/16208)) +- fix(node): Fix 404 route handling in express 5 ([#16211](https://github.com/getsentry/sentry-javascript/pull/16211)) +- fix(logs): Ensure logs can be flushed correctly ([#16216](https://github.com/getsentry/sentry-javascript/pull/16216)) +- ref(core): Switch to standardized log envelope ([#16133](https://github.com/getsentry/sentry-javascript/pull/16133)) + +## 9.15.0 + +### Important Changes + +- **feat: Export `wrapMcpServerWithSentry` from server packages ([#16127](https://github.com/getsentry/sentry-javascript/pull/16127))** + +Exports the wrapMcpServerWithSentry which is our MCP server instrumentation from all the server packages. + +- **feat(core): Associate resource/tool/prompt invocations with request span instead of response span ([#16126](https://github.com/getsentry/sentry-javascript/pull/16126))** + +Adds a best effort mechanism to associate handler spans for `resource`, `tool` and `prompt` with the incoming message requests instead of the outgoing SSE response. + +### Other Changes + +- fix: Vercel `ai` ESM patching ([#16152](https://github.com/getsentry/sentry-javascript/pull/16152)) +- fix(node): Update version range for `module.register` ([#16125](https://github.com/getsentry/sentry-javascript/pull/16125)) +- fix(react-router): Spread `unstable_sentryVitePluginOptions` correctly ([#16156](https://github.com/getsentry/sentry-javascript/pull/16156)) +- fix(react): Fix Redux integration failing with reducer injection ([#16106](https://github.com/getsentry/sentry-javascript/pull/16106)) +- fix(remix): Add ESM-compatible exports ([#16124](https://github.com/getsentry/sentry-javascript/pull/16124)) +- fix(remix): Avoid rewrapping root loader. ([#16136](https://github.com/getsentry/sentry-javascript/pull/16136)) + +Work in this release was contributed by @AntoineDuComptoirDesPharmacies. Thank you for your contribution! + +## 9.14.0 + +### Important Changes + +- **feat: Add Supabase Integration ([#15719](https://github.com/getsentry/sentry-javascript/pull/15719))** + +This PR adds Supabase integration to `@sentry/core`, allowing automatic instrumentation of Supabase client operations (database queries and authentication) for performance monitoring and error tracking. + +- **feat(nestjs): Gracefully handle RPC scenarios in `SentryGlobalFilter` ([#16066](https://github.com/getsentry/sentry-javascript/pull/16066))** + +This PR adds better RPC exception handling to `@sentry/nestjs`, preventing application crashes while still capturing errors and warning users when a dedicated filter is needed. The implementation gracefully handles the 'rpc' context type in `SentryGlobalFilter` to improve reliability in hybrid applications. + +- **feat(react-router): Trace propagation ([#16070](https://github.com/getsentry/sentry-javascript/pull/16070))** + +This PR adds trace propagation to `@sentry/react-router` by providing utilities to inject trace meta tags into HTML headers and offering a pre-built Sentry-instrumented request handler, improving distributed tracing capabilities across page loads. + +### Other Changes + +- feat(deps): Bump @prisma/instrumentation from 6.5.0 to 6.6.0 ([#16102](https://github.com/getsentry/sentry-javascript/pull/16102)) +- feat(nextjs): Improve server component data ([#15996](https://github.com/getsentry/sentry-javascript/pull/15996)) +- feat(nuxt): Log when adding HTML trace meta tags ([#16044](https://github.com/getsentry/sentry-javascript/pull/16044)) +- fix(node): Make body capturing more robust ([#16105](https://github.com/getsentry/sentry-javascript/pull/16105)) +- ref(node): Log when incoming request bodies are being captured ([#16104](https://github.com/getsentry/sentry-javascript/pull/16104)) + +## 9.13.0 + +### Important Changes + +- **feat(node): Add support for winston logger ([#15983](https://github.com/getsentry/sentry-javascript/pull/15983))** + + Sentry is adding support for [structured logging](https://github.com/getsentry/sentry-javascript/discussions/15916). In this release we've added support for sending logs to Sentry via the [winston](https://github.com/winstonjs/winston) logger to the Sentry Node SDK (and SDKs that use the Node SDK under the hood like `@sentry/nestjs`). The Logging APIs in the Sentry SDK are still experimental and subject to change. + + ```js + const winston = require('winston'); + const Transport = require('winston-transport'); + + const transport = Sentry.createSentryWinstonTransport(Transport); + + const logger = winston.createLogger({ + transports: [transport], + }); + ``` + +- **feat(core): Add `wrapMcpServerWithSentry` to instrument MCP servers from `@modelcontextprotocol/sdk` ([#16032](https://github.com/getsentry/sentry-javascript/pull/16032))** + + The Sentry SDK now supports instrumenting MCP servers from the `@modelcontextprotocol/sdk` package. Compatible with versions `^1.9.0` of the `@modelcontextprotocol/sdk` package. + + ```js + import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; + + // Create an MCP server + const server = new McpServer({ + name: 'Demo', + version: '1.0.0', + }); + + // Use the instrumented server in your application + const instrumentedServer = Sentry.wrapMcpServerWithSentry(server); + ``` + +- **feat(core): Move console integration into core and add to cloudflare/vercel-edge ([#16024](https://github.com/getsentry/sentry-javascript/pull/16024))** + + Console instrumentation has been added to `@sentry/cloudflare` and `@sentry/nextjs` Edge Runtime and is enabled by default. Now calls to the console object will be captured as breadcrumbs for those SDKs. + +- **feat(bun): Support new `Bun.serve` APIs ([#16035](https://github.com/getsentry/sentry-javascript/pull/16035))** + + Bun `1.2.6` and above have a new `Bun.serve` API, which the Bun SDK now supports. The SDK instruments the new routes object that can be used to define routes for the server. + + Thanks to @Jarred-Sumner for helping us get this supported! + +### Other Changes + +- feat(browser): Warn on duplicate `browserTracingIntegration` ([#16042](https://github.com/getsentry/sentry-javascript/pull/16042)) +- feat(core): Allow delayed sending with offline transport ([#15937](https://github.com/getsentry/sentry-javascript/pull/15937)) +- feat(deps): Bump @sentry/webpack-plugin from 3.2.4 to 3.3.1 ([#16057](https://github.com/getsentry/sentry-javascript/pull/16057)) +- feat(vue): Apply stateTransformer to attachments in Pinia Plugin ([#16034](https://github.com/getsentry/sentry-javascript/pull/16034)) +- fix(core): Run `beforeSendLog` after we process log ([#16019](https://github.com/getsentry/sentry-javascript/pull/16019)) +- fix(nextjs): Don't show turbopack warning for newer Next.js canaries ([#16065](https://github.com/getsentry/sentry-javascript/pull/16065)) +- fix(nextjs): Include patch version 0 for min supported 15.3.0 ([#16026](https://github.com/getsentry/sentry-javascript/pull/16026)) +- fix(node): Ensure late init works with all integrations ([#16016](https://github.com/getsentry/sentry-javascript/pull/16016)) +- fix(react-router): Pass `unstable_sentryVitePluginOptions` to cli instance ([#16033](https://github.com/getsentry/sentry-javascript/pull/16033)) +- fix(serverless-aws): Overwrite root span name with GraphQL if set ([#16010](https://github.com/getsentry/sentry-javascript/pull/16010)) + +## 9.12.0 + +### Important Changes + +- **feat(feedback): Implement highlighting and hiding controls for screenshots ([#15951](https://github.com/getsentry/sentry-javascript/pull/15951))** + + The Sentry SDK now supports highlighting and hiding controls for screenshots in [user feedback reports](https://docs.sentry.io/platforms/javascript/user-feedback/). This functionality is enabled by default. + +- **feat(node): Add `ignoreIncomingRequestBody` callback to `httpIntegration` ([#15959](https://github.com/getsentry/sentry-javascript/pull/15959))** + + The `httpIntegration` now supports an optional `ignoreIncomingRequestBody` callback that can be used to skip capturing the body of incoming requests. + + ```ts + Sentry.init({ + integrations: [ + Sentry.httpIntegration({ + ignoreIncomingRequestBody: (url, request) => { + return request.method === 'GET' && url.includes('/api/large-payload'); + }, + }), + ], + }); + ``` + + The `ignoreIncomingRequestBody` callback receives the URL of the request and should return `true` if the body should be ignored. + +- **Logging Improvements** + + Sentry is adding support for [structured logging](https://github.com/getsentry/sentry-javascript/discussions/15916). In this release we've made a variety of improvements to logging functionality in the Sentry SDKs. + - feat(node): Add server.address to nodejs logs ([#16006](https://github.com/getsentry/sentry-javascript/pull/16006)) + - feat(core): Add sdk name and version to logs ([#16005](https://github.com/getsentry/sentry-javascript/pull/16005)) + - feat(core): Add sentry origin attribute to console logs integration ([#15998](https://github.com/getsentry/sentry-javascript/pull/15998)) + - fix(core): Do not abbreviate message parameter attribute ([#15987](https://github.com/getsentry/sentry-javascript/pull/15987)) + - fix(core): Prefix release and environment correctly ([#15999](https://github.com/getsentry/sentry-javascript/pull/15999)) + - fix(node): Make log flushing logic more robust ([#15991](https://github.com/getsentry/sentry-javascript/pull/15991)) + +### Other Changes + +- build(aws-serverless): Include debug logs in lambda layer SDK bundle ([#15974](https://github.com/getsentry/sentry-javascript/pull/15974)) +- feat(astro): Add tracking of errors during HTML streaming ([#15995](https://github.com/getsentry/sentry-javascript/pull/15995)) +- feat(browser): Add `onRequestSpanStart` hook to browser tracing integration ([#15979](https://github.com/getsentry/sentry-javascript/pull/15979)) +- feat(deps): Bump @sentry/cli from 2.42.3 to 2.43.0 ([#16001](https://github.com/getsentry/sentry-javascript/pull/16001)) +- feat(nextjs): Add `captureRouterTransitionStart` hook for capturing navigations ([#15981](https://github.com/getsentry/sentry-javascript/pull/15981)) +- feat(nextjs): Mark clientside prefetch request spans with `http.request.prefetch: true` attribute ([#15980](https://github.com/getsentry/sentry-javascript/pull/15980)) +- feat(nextjs): Un experimentify `clientInstrumentationHook` ([#15992](https://github.com/getsentry/sentry-javascript/pull/15992)) +- feat(nextjs): Warn when client was initialized more than once ([#15971](https://github.com/getsentry/sentry-javascript/pull/15971)) +- feat(node): Add support for `SENTRY_DEBUG` env variable ([#15972](https://github.com/getsentry/sentry-javascript/pull/15972)) +- fix(tss-react): Change `authToken` type to `string` ([#15985](https://github.com/getsentry/sentry-javascript/pull/15985)) + +Work in this release was contributed by @Page- and @Fryuni. Thank you for your contributions! + +## 9.11.0 + +- feat(browser): Add `http.redirect_count` attribute to `browser.redirect` span ([#15943](https://github.com/getsentry/sentry-javascript/pull/15943)) +- feat(core): Add `consoleLoggingIntegration` for logs ([#15955](https://github.com/getsentry/sentry-javascript/pull/15955)) +- feat(core): Don't truncate error messages ([#15818](https://github.com/getsentry/sentry-javascript/pull/15818)) +- feat(core): Emit debug log when transport execution fails ([#16009](https://github.com/getsentry/sentry-javascript/pull/16009)) +- feat(nextjs): Add release injection in Turbopack ([#15958](https://github.com/getsentry/sentry-javascript/pull/15958)) +- feat(nextjs): Record `turbopack` as tag ([#15928](https://github.com/getsentry/sentry-javascript/pull/15928)) +- feat(nuxt): Base decision on source maps upload only on Nuxt source map settings ([#15859](https://github.com/getsentry/sentry-javascript/pull/15859)) +- feat(react-router): Add `sentryHandleRequest` ([#15787](https://github.com/getsentry/sentry-javascript/pull/15787)) +- fix(node): Use `module` instead of `require` for CJS check ([#15927](https://github.com/getsentry/sentry-javascript/pull/15927)) +- fix(remix): Remove mentions of deprecated `ErrorBoundary` wrapper ([#15930](https://github.com/getsentry/sentry-javascript/pull/15930)) +- ref(browser): Temporarily add `sentry.previous_trace` span attribute ([#15957](https://github.com/getsentry/sentry-javascript/pull/15957)) +- ref(browser/core): Move all log flushing logic into clients ([#15831](https://github.com/getsentry/sentry-javascript/pull/15831)) +- ref(core): Improve URL parsing utilities ([#15882](https://github.com/getsentry/sentry-javascript/pull/15882)) + +## 9.10.1 + +- fix: Correct @sentry-internal/feedback docs to match the code ([#15874](https://github.com/getsentry/sentry-javascript/pull/15874)) +- deps: Bump bundler plugins to version `3.2.4` ([#15909](https://github.com/getsentry/sentry-javascript/pull/15909)) + +## 9.10.0 + +### Important Changes + +- **feat: Add support for logs** + - feat(node): Add logging public APIs to Node SDKs ([#15764](https://github.com/getsentry/sentry-javascript/pull/15764)) + - feat(core): Add support for `beforeSendLog` ([#15814](https://github.com/getsentry/sentry-javascript/pull/15814)) + - feat(core): Add support for parameterizing logs ([#15812](https://github.com/getsentry/sentry-javascript/pull/15812)) + - fix: Remove critical log severity level ([#15824](https://github.com/getsentry/sentry-javascript/pull/15824)) + + All JavaScript SDKs other than `@sentry/cloudflare` and `@sentry/deno` now support sending logs via dedicated methods as part of Sentry's [upcoming logging product](https://github.com/getsentry/sentry/discussions/86804). + + Logging is gated by an experimental option, `_experiments.enableLogs`. + + ```js + Sentry.init({ + dsn: 'PUBLIC_DSN', + // `enableLogs` must be set to true to use the logging features + _experiments: { enableLogs: true }, + }); + + const { trace, debug, info, warn, error, fatal, fmt } = Sentry.logger; + + trace('Starting database connection', { database: 'users' }); + debug('Cache miss for user', { userId: 123 }); + error('Failed to process payment', { orderId: 'order_123', amount: 99.99 }); + fatal('Database connection pool exhausted', { database: 'users', activeConnections: 100 }); + + // Structured logging via the `fmt` helper function. When you use `fmt`, the string template and parameters are sent separately so they can be queried independently in Sentry. + + info(fmt(`Updated profile for user ${userId}`)); + warn(fmt(`Rate limit approaching for endpoint ${endpoint}. Requests: ${requests}, Limit: ${limit}`)); + ``` + + With server-side SDKs like `@sentry/node`, `@sentry/bun` or server-side of `@sentry/nextjs` or `@sentry/sveltekit`, you can do structured logging without needing the `fmt` helper function. + + ```js + const { info, warn } = Sentry.logger; + + info('User %s logged in successfully', [123]); + warn('Failed to load user %s data', [123], { errorCode: 404 }); + ``` + + To filter logs, or update them before they are sent to Sentry, you can use the `_experiments.beforeSendLog` option. + +- **feat(browser): Add `diagnoseSdkConnectivity()` function to programmatically detect possible connectivity issues ([#15821](https://github.com/getsentry/sentry-javascript/pull/15821))** + + The `diagnoseSdkConnectivity()` function can be used to programmatically detect possible connectivity issues with the Sentry SDK. + + ```js + const result = await Sentry.diagnoseSdkConnectivity(); + ``` + + The result will be an object with the following properties: + - `"no-client-active"`: There was no active client when the function was called. This possibly means that the SDK was not initialized yet. + - `"sentry-unreachable"`: The Sentry SaaS servers were not reachable. This likely means that there is an ad blocker active on the page or that there are other connection issues. + - `undefined`: The SDK is working as expected. + +- **SDK Tracing Performance Improvements for Node SDKs** + - feat: Stop using `dropUndefinedKeys` ([#15796](https://github.com/getsentry/sentry-javascript/pull/15796)) + - feat(node): Only add span listeners for instrumentation when used ([#15802](https://github.com/getsentry/sentry-javascript/pull/15802)) + - ref: Avoid `dropUndefinedKeys` for `spanToJSON` calls ([#15792](https://github.com/getsentry/sentry-javascript/pull/15792)) + - ref: Avoid using `SentryError` for PromiseBuffer control flow ([#15822](https://github.com/getsentry/sentry-javascript/pull/15822)) + - ref: Stop using `dropUndefinedKeys` in SpanExporter ([#15794](https://github.com/getsentry/sentry-javascript/pull/15794)) + - ref(core): Avoid using `SentryError` for event processing control flow ([#15823](https://github.com/getsentry/sentry-javascript/pull/15823)) + - ref(node): Avoid `dropUndefinedKeys` in Node SDK init ([#15797](https://github.com/getsentry/sentry-javascript/pull/15797)) + - ref(opentelemetry): Avoid sampling work for non-root spans ([#15820](https://github.com/getsentry/sentry-javascript/pull/15820)) + + We've been hard at work making performance improvements to the Sentry Node SDKs (`@sentry/node`, `@sentry/aws-serverless`, `@sentry/nestjs`, etc.). We've seen that upgrading from `9.7.0` to `9.10.0` leads to 30-40% improvement in request latency for HTTP web-server applications that use tracing with high sample rates. Non web-server applications and non-tracing applications will see smaller improvements. + +### Other Changes + +- chore(deps): Bump `rrweb` to `2.35.0` ([#15825](https://github.com/getsentry/sentry-javascript/pull/15825)) +- deps: Bump bundler plugins to `3.2.3` ([#15829](https://github.com/getsentry/sentry-javascript/pull/15829)) +- feat: Always truncate stored breadcrumb messages to 2kb ([#15819](https://github.com/getsentry/sentry-javascript/pull/15819)) +- feat(nextjs): Disable server webpack-handling for static builds ([#15751](https://github.com/getsentry/sentry-javascript/pull/15751)) +- fix(nuxt): Don't override Nuxt options if undefined ([#15795](https://github.com/getsentry/sentry-javascript/pull/15795)) + +## 9.9.0 + +### Important Changes + +- **feat(nextjs): Support `instrumentation-client.ts` ([#15705](https://github.com/getsentry/sentry-javascript/pull/15705))** + + Next.js recently added a feature to support [client-side (browser) instrumentation via a `instrumentation-client.ts` file](https://nextjs.org/docs/app/api-reference/file-conventions/instrumentation-client). + + To be forwards compatible, the Sentry Next.js SDK will now pick up `instrumentation-client.ts` files even on older Next.js versions and add them to your client bundles. + It is suggested that you either rename your `sentry.client.config.ts` file to `instrumentation-client.ts`, or if you already happen to have a `instrumentation-client.ts` file move the contents of `sentry.client.config.ts` to `instrumentation-client.ts`. + +- **feat(browser): Add `previous_trace` span links ([#15569](https://github.com/getsentry/sentry-javascript/pull/15569))** + + The `@sentry/browser` SDK and SDKs based on `@sentry/browser` now emits a link from the first root span of a newly started trace to the root span of a previously started trace. You can control this feature via an option in `browserTracingIntegration()`: + + ```js + Sentry.init({ + dsn: 'your-dsn-here' + integrations: [ + Sentry.browserTracingIntegration({ + // Available settings: + // - 'in-memory' (default): Stores previous trace information in memory + // - 'session-storage': Stores previous trace information in the browser's `sessionStorage` + // - 'off': Disable storing and sending previous trace information + linkPreviousTrace: 'in-memory', + }), + ], + }); + ``` + +- **feat(browser): Add `logger.X` methods to browser SDK ([#15763](https://github.com/getsentry/sentry-javascript/pull/15763))** + + For Sentry's [upcoming logging product](https://github.com/getsentry/sentry/discussions/86804), the SDK now supports sending logs via dedicated methods. + + ```js + Sentry.init({ + dsn: 'your-dsn-here', + _experiments: { + enableLogs: true, // This is required to use the logging features + }, + }); + + Sentry.logger.info('This is a trace message', { userId: 123 }); + // See PR for better documentation + ``` + + Please note that the logs product is still in early access. See the link above for more information. + +### Other Changes + +- feat(browser): Attach host as part of error message to "Failed to fetch" errors ([#15729](https://github.com/getsentry/sentry-javascript/pull/15729)) +- feat(core): Add `parseStringToURL` method ([#15768](https://github.com/getsentry/sentry-javascript/pull/15768)) +- feat(core): Optimize `dropUndefinedKeys` ([#15760](https://github.com/getsentry/sentry-javascript/pull/15760)) +- feat(node): Add fastify `shouldHandleError` ([#15771](https://github.com/getsentry/sentry-javascript/pull/15771)) +- fix(nuxt): Delete no longer needed Nitro 'close' hook ([#15790](https://github.com/getsentry/sentry-javascript/pull/15790)) +- perf(nestjs): Remove usage of `addNonEnumerableProperty` ([#15766](https://github.com/getsentry/sentry-javascript/pull/15766)) +- ref: Avoid some usage of `dropUndefinedKeys()` ([#15757](https://github.com/getsentry/sentry-javascript/pull/15757)) +- ref: Remove some usages of `dropUndefinedKeys()` ([#15781](https://github.com/getsentry/sentry-javascript/pull/15781)) +- ref(nextjs): Fix Next.js vercel-edge runtime package information ([#15789](https://github.com/getsentry/sentry-javascript/pull/15789)) + +## 9.8.0 + +- feat(node): Implement new continuous profiling API spec ([#15635](https://github.com/getsentry/sentry-javascript/pull/15635)) +- feat(profiling): Add platform to chunk envelope ([#15758](https://github.com/getsentry/sentry-javascript/pull/15758)) +- feat(react): Export captureReactException method ([#15746](https://github.com/getsentry/sentry-javascript/pull/15746)) +- fix(node): Check for `res.end` before passing to Proxy ([#15776](https://github.com/getsentry/sentry-javascript/pull/15776)) +- perf(core): Add short-circuits to `eventFilters` integration ([#15752](https://github.com/getsentry/sentry-javascript/pull/15752)) +- perf(node): Short circuit flushing on Vercel only for Vercel ([#15734](https://github.com/getsentry/sentry-javascript/pull/15734)) + +## 9.7.0 + +- feat(core): Add `captureLog` method ([#15717](https://github.com/getsentry/sentry-javascript/pull/15717)) +- feat(remix/cloudflare): Export `sentryHandleError` ([#15726](https://github.com/getsentry/sentry-javascript/pull/15726)) +- fix(node): Always flush on Vercel before Lambda freeze ([#15602](https://github.com/getsentry/sentry-javascript/pull/15602)) +- fix(node): Ensure incoming traces are propagated without HttpInstrumentation ([#15732](https://github.com/getsentry/sentry-javascript/pull/15732)) +- fix(node): Use `fatal` level for unhandled rejections in `strict` mode ([#15720](https://github.com/getsentry/sentry-javascript/pull/15720)) +- fix(nuxt): Delete Nuxt server template injection ([#15749](https://github.com/getsentry/sentry-javascript/pull/15749)) + +## 9.6.1 + +- feat(deps): bump @prisma/instrumentation from 6.4.1 to 6.5.0 ([#15714](https://github.com/getsentry/sentry-javascript/pull/15714)) +- feat(deps): bump @sentry/cli from 2.42.2 to 2.42.3 ([#15711](https://github.com/getsentry/sentry-javascript/pull/15711)) +- fix(nextjs): Re-patch router if it is overridden by Next.js ([#15721](https://github.com/getsentry/sentry-javascript/pull/15721)) +- fix(nuxt): Add Nitro Rollup plugin to inject Sentry server config ([#15710](https://github.com/getsentry/sentry-javascript/pull/15710)) +- chore(deps): Bump rollup to 4.35.0 ([#15651](https://github.com/getsentry/sentry-javascript/pull/15651)) + +## 9.6.0 + +### Important Changes + +- **feat(tanstackstart): Add `@sentry/tanstackstart-react` package and make `@sentry/tanstackstart` package a utility package ([#15629](https://github.com/getsentry/sentry-javascript/pull/15629))** + + Since TanStack Start is supposed to be a generic framework that supports libraries like React and Solid, the `@sentry/tanstackstart` SDK package was renamed to `@sentry/tanstackstart-react` to reflect that the SDK is specifically intended to be used for React TanStack Start applications. + Note that the TanStack Start SDK is still in alpha status and may be subject to breaking changes in non-major package updates. + +### Other Changes + +- feat(astro): Accept all vite-plugin options ([#15638](https://github.com/getsentry/sentry-javascript/pull/15638)) +- feat(deps): bump @sentry/webpack-plugin from 3.2.1 to 3.2.2 ([#15627](https://github.com/getsentry/sentry-javascript/pull/15627)) +- feat(tanstackstart): Refine initial API ([#15574](https://github.com/getsentry/sentry-javascript/pull/15574)) +- fix(core): Ensure `fill` only patches functions ([#15632](https://github.com/getsentry/sentry-javascript/pull/15632)) +- fix(nextjs): Consider `pageExtensions` when looking for instrumentation file ([#15701](https://github.com/getsentry/sentry-javascript/pull/15701)) +- fix(remix): Null-check `options` ([#15610](https://github.com/getsentry/sentry-javascript/pull/15610)) +- fix(sveltekit): Correctly parse angle bracket type assertions for auto instrumentation ([#15578](https://github.com/getsentry/sentry-javascript/pull/15578)) +- fix(sveltekit): Guard process variable ([#15605](https://github.com/getsentry/sentry-javascript/pull/15605)) + +Work in this release was contributed by @angelikatyborska and @nwalters512. Thank you for your contributions! + +## 9.5.0 + +### Important Changes + +We found some issues with the new feedback screenshot annotation where screenshots are not being generated properly. Due to this issue, we are reverting the feature. + +- Revert "feat(feedback) Allowing annotation via highlighting & masking ([#15484](https://github.com/getsentry/sentry-javascript/pull/15484))" (#15609) + +### Other Changes + +- Add cloudflare adapter detection and path generation ([#15603](https://github.com/getsentry/sentry-javascript/pull/15603)) +- deps(nextjs): Bump rollup to `4.34.9` ([#15589](https://github.com/getsentry/sentry-javascript/pull/15589)) +- feat(bun): Automatically add performance integrations ([#15586](https://github.com/getsentry/sentry-javascript/pull/15586)) +- feat(replay): Bump rrweb to 2.34.0 ([#15580](https://github.com/getsentry/sentry-javascript/pull/15580)) +- fix(browser): Call original function on early return from patched history API ([#15576](https://github.com/getsentry/sentry-javascript/pull/15576)) +- fix(nestjs): Copy metadata in custom decorators ([#15598](https://github.com/getsentry/sentry-javascript/pull/15598)) +- fix(react-router): Fix config type import ([#15583](https://github.com/getsentry/sentry-javascript/pull/15583)) +- fix(remix): Use correct types export for `@sentry/remix/cloudflare` ([#15599](https://github.com/getsentry/sentry-javascript/pull/15599)) +- fix(vue): Attach Pinia state only once per event ([#15588](https://github.com/getsentry/sentry-javascript/pull/15588)) + +Work in this release was contributed by @msurdi-a8c, @namoscato, and @rileyg98. Thank you for your contributions! + +## 9.4.0 + +- feat(core): Add types for logs protocol and envelope ([#15530](https://github.com/getsentry/sentry-javascript/pull/15530)) +- feat(deps): Bump `@sentry/cli` from 2.41.1 to 2.42.2 ([#15510](https://github.com/getsentry/sentry-javascript/pull/15510)) +- feat(deps): Bump `@sentry/webpack-plugin` from 3.1.2 to 3.2.1 ([#15512](https://github.com/getsentry/sentry-javascript/pull/15512)) +- feat(feedback) Allowing annotation via highlighting & masking ([#15484](https://github.com/getsentry/sentry-javascript/pull/15484)) +- feat(nextjs): Add `use client` directive to client SDK entrypoints ([#15575](https://github.com/getsentry/sentry-javascript/pull/15575)) +- feat(nextjs): Allow silencing of instrumentation warning ([#15555](https://github.com/getsentry/sentry-javascript/pull/15555)) +- feat(sveltekit): Ensure `AsyncLocalStorage` async context strategy is used in Cloudflare Pages ([#15557](https://github.com/getsentry/sentry-javascript/pull/15557)) +- fix(cloudflare): Make `@cloudflare/workers-types` an optional peer dependency ([#15554](https://github.com/getsentry/sentry-javascript/pull/15554)) +- fix(core): Don't reverse values in event filters ([#15584](https://github.com/getsentry/sentry-javascript/pull/15584)) +- fix(core): Handle normalization of null prototypes correctly ([#15556](https://github.com/getsentry/sentry-javascript/pull/15556)) +- fix(nextjs): Only warn on missing `onRequestError` in version 15 ([#15553](https://github.com/getsentry/sentry-javascript/pull/15553)) +- fix(node): Allow for `undefined` transport to be passed in ([#15560](https://github.com/getsentry/sentry-javascript/pull/15560)) +- fix(wasm): Fix wasm integration stacktrace parsing for filename ([#15572](https://github.com/getsentry/sentry-javascript/pull/15572)) +- perf(node): Store normalized request for processing ([#15570](https://github.com/getsentry/sentry-javascript/pull/15570)) + +## 9.3.0 + +### Important Changes + +With this release we're publishing two new SDKs in **experimental alpha** stage: + +- **feat(tanstackstart): Add TanStack Start SDK ([#15523](https://github.com/getsentry/sentry-javascript/pull/15523))** + +For details please refer to the [README](https://github.com/getsentry/sentry-javascript/tree/develop/packages/tanstackstart) + +- **feat(react-router): Add React Router SDK ([#15524](https://github.com/getsentry/sentry-javascript/pull/15524))** + +For details please refer to the [README](https://github.com/getsentry/sentry-javascript/tree/develop/packages/react-router) + +- **feat(remix): Add support for Hydrogen ([#15450](https://github.com/getsentry/sentry-javascript/pull/15450))** + +This PR adds support for Shopify Hydrogen applications running on MiniOxygen runtime. + +### Other Changes + +- feat(core): Add `forceTransaction` to trpc middleware options ([#15519](https://github.com/getsentry/sentry-javascript/pull/15519)) +- feat(core): Default filter unactionable error ([#15527](https://github.com/getsentry/sentry-javascript/pull/15527)) +- feat(core): Rename `inboundFiltersIntegration` to `eventFiltersIntegration` ([#15434](https://github.com/getsentry/sentry-javascript/pull/15434)) +- feat(deps): bump @prisma/instrumentation from 6.2.1 to 6.4.1 ([#15480](https://github.com/getsentry/sentry-javascript/pull/15480)) +- feat(react-router): Add build-time config ([#15406](https://github.com/getsentry/sentry-javascript/pull/15406)) +- feat(replay): Bump rrweb to 2.33.0 ([#15514](https://github.com/getsentry/sentry-javascript/pull/15514)) +- fix(core): Fix `allowUrls` and `denyUrls` for linked and aggregate exceptions ([#15521](https://github.com/getsentry/sentry-javascript/pull/15521)) +- fix(nextjs): Don't capture devmode server-action redirect errors ([#15485](https://github.com/getsentry/sentry-javascript/pull/15485)) +- fix(nextjs): warn about missing onRequestError handler [#15488](https://github.com/getsentry/sentry-javascript/pull/15488)) +- fix(nextjs): Prevent wrong culprit from showing up for clientside error events [#15475](https://github.com/getsentry/sentry-javascript/pull/15475)) +- fix(nuxt): Ignore 300-400 status codes on app errors in Nuxt ([#15473](https://github.com/getsentry/sentry-javascript/pull/15473)) +- fix(react): Add support for cross-usage of React Router instrumentations ([#15283](https://github.com/getsentry/sentry-javascript/pull/15283)) +- fix(sveltekit): Guard `process` check when flushing events ([#15516](https://github.com/getsentry/sentry-javascript/pull/15516)) + +Work in this release was contributed by @GerryWilko and @leoambio. Thank you for your contributions! + +## 9.2.0 + +### Important Changes + +- **feat(node): Support Express v5 ([#15380](https://github.com/getsentry/sentry-javascript/pull/15380))** + +This release adds full tracing support for Express v5, and improves tracing support for Nest.js 11 (which uses Express v5) in the Nest.js SDK. + +- **feat(sveltekit): Add Support for Cloudflare ([#14672](https://github.com/getsentry/sentry-javascript/pull/14672))** + +This release adds support for deploying SvelteKit applications to Cloudflare Pages. +A docs update with updated instructions will follow shortly. +Until then, you can give this a try by setting up the SvelteKit SDK as usual and then following the instructions outlined in the PR. + +Thank you @SG60 for contributing this feature! + +### Other Changes + +- feat(core): Add `addLink(s)` to Sentry span ([#15452](https://github.com/getsentry/sentry-javascript/pull/15452)) +- feat(core): Add links to span options ([#15453](https://github.com/getsentry/sentry-javascript/pull/15453)) +- feat(deps): Bump @sentry/webpack-plugin from 2.22.7 to 3.1.2 ([#15328](https://github.com/getsentry/sentry-javascript/pull/15328)) +- feat(feedback): Disable Feedback submit & cancel buttons while submitting ([#15408](https://github.com/getsentry/sentry-javascript/pull/15408)) +- feat(nextjs): Add experimental flag to not strip origin information from different origin stack frames ([#15418](https://github.com/getsentry/sentry-javascript/pull/15418)) +- feat(nuxt): Add `enableNitroErrorHandler` to server options ([#15444](https://github.com/getsentry/sentry-javascript/pull/15444)) +- feat(opentelemetry): Add `addLink(s)` to span ([#15387](https://github.com/getsentry/sentry-javascript/pull/15387)) +- feat(opentelemetry): Add `links` to span options ([#15403](https://github.com/getsentry/sentry-javascript/pull/15403)) +- feat(replay): Expose rrweb recordCrossOriginIframes under \_experiments ([#14916](https://github.com/getsentry/sentry-javascript/pull/14916)) +- fix(browser): Ensure that `performance.measure` spans have a positive duration ([#15415](https://github.com/getsentry/sentry-javascript/pull/15415)) +- fix(bun): Includes correct sdk metadata ([#15459](https://github.com/getsentry/sentry-javascript/pull/15459)) +- fix(core): Add Google `gmo` error to Inbound Filters ([#15432](https://github.com/getsentry/sentry-javascript/pull/15432)) +- fix(core): Ensure `http.client` span descriptions don't contain query params or fragments ([#15404](https://github.com/getsentry/sentry-javascript/pull/15404)) +- fix(core): Filter out unactionable Facebook Mobile browser error ([#15430](https://github.com/getsentry/sentry-javascript/pull/15430)) +- fix(nestjs): Pin dependency on `@opentelemetry/instrumentation` ([#15419](https://github.com/getsentry/sentry-javascript/pull/15419)) +- fix(nuxt): Only use filename with file extension from command ([#15445](https://github.com/getsentry/sentry-javascript/pull/15445)) +- fix(nuxt): Use `SentryNuxtServerOptions` type for server init ([#15441](https://github.com/getsentry/sentry-javascript/pull/15441)) +- fix(sveltekit): Avoid loading vite config to determine source maps setting ([#15440](https://github.com/getsentry/sentry-javascript/pull/15440)) +- ref(profiling-node): Bump chunk interval to 60s ([#15361](https://github.com/getsentry/sentry-javascript/pull/15361)) + +Work in this release was contributed by @6farer, @dgavranic and @SG60. Thank you for your contributions! + +## 9.1.0 + +- feat(browser): Add `graphqlClientIntegration` ([#13783](https://github.com/getsentry/sentry-javascript/pull/13783)) +- feat(core): Allow for nested trpc context ([#15379](https://github.com/getsentry/sentry-javascript/pull/15379)) +- feat(core): Create types and utilities for span links ([#15375](https://github.com/getsentry/sentry-javascript/pull/15375)) +- feat(deps): bump @opentelemetry/instrumentation-pg from 0.50.0 to 0.51.0 ([#15273](https://github.com/getsentry/sentry-javascript/pull/15273)) +- feat(node): Extract Sentry-specific node-fetch instrumentation ([#15231](https://github.com/getsentry/sentry-javascript/pull/15231)) +- feat(vue): Support Pinia v3 ([#15383](https://github.com/getsentry/sentry-javascript/pull/15383)) +- fix(sveltekit): Avoid request body double read errors ([#15368](https://github.com/getsentry/sentry-javascript/pull/15368)) +- fix(sveltekit): Avoid top-level `vite` import ([#15371](https://github.com/getsentry/sentry-javascript/pull/15371)) + +Work in this release was contributed by @Zen-cronic and @filips-alpe. Thank you for your contribution! + +## 9.0.1 + +- ref(flags): rename unleash integration param ([#15343](https://github.com/getsentry/sentry-javascript/pull/15343)) + +## 9.0.0 + +Version `9.0.0` marks a release of the Sentry JavaScript SDKs that contains breaking changes. +The goal of this release is to trim down on unused and potentially confusing APIs, prepare the SDKs for future framework versions to build deeper instrumentation, and remove old polyfills to reduce the packages' size. + +### How To Upgrade + +Please carefully read through the migration guide in the Sentry docs on how to upgrade from version 8 to version 9. +Make sure to select your specific platform/framework in the top left corner: https://docs.sentry.io/platforms/javascript/migration/v8-to-v9/ + +A comprehensive migration guide outlining all changes for all the frameworks can be found within the Sentry JavaScript SDK Repository: https://github.com/getsentry/sentry-javascript/blob/develop/MIGRATION.md + +### Breaking Changes + +- doc(deno)!: Make Deno v2 the minimum supported version (#15085) +- feat!: Bump typescript to `~5.0.0` (#14758) +- feat!: Drop `nitro-utils` package (#14998) +- feat!: Only collect ip addresses with `sendDefaultPii: true` (#15084) +- feat!: Remove `autoSessionTracking` option (#14802) +- feat!: Remove `enableTracing` (#15078) +- feat!: Remove `getCurrentHub()`, `Hub`, and `getCurrentHubShim()` (#15122) +- feat!: Remove `spanId` from propagation context (#14733) +- feat!: Remove deprecated and unused code (#15077) +- feat!: Remove metrics API from the JS SDK (#14745) +- feat!: Require Node `>=18` as minimum supported version (#14749) +- feat(astro)!: Respect user-specified source map setting (#14941) +- feat(browser)!: Remove `captureUserFeedback` method (#14820) +- feat(build)!: Drop pre-ES2020 polyfills (#14882) +- feat(core)!: Add `normalizedRequest` to `samplingContext` (#14902) +- feat(core)!: Always use session from isolation scope (#14860) +- feat(core)!: Pass root spans to `beforeSendSpan` and disallow returning `null` (#14831) +- feat(core)!: Remove `BAGGAGE_HEADER_NAME` export (#14785) +- feat(core)!: Remove `TransactionNamingScheme` type (#14865) +- feat(core)!: Remove `addOpenTelemetryInstrumentation` method (#14792) +- feat(core)!: Remove `arrayify` method (#14782) +- feat(core)!: Remove `debugIntegration` and `sessionTimingIntegration` (#14747) +- feat(core)!: Remove `flatten` method (#14784) +- feat(core)!: Remove `getDomElement` method (#14797) +- feat(core)!: Remove `makeFifoCache` method (#14786) +- feat(core)!: Remove `memoBuilder` export & `WeakSet` fallback (#14859) +- feat(core)!: Remove `transactionContext` from `samplingContext` (#14904) +- feat(core)!: Remove `urlEncode` method (#14783) +- feat(core)!: Remove deprecated `Request` type (#14858) +- feat(core)!: Remove deprecated request data methods (#14896) +- feat(core)!: Remove standalone `Client` interface & deprecate `BaseClient` (#14800) +- feat(core)!: Remove validSeverityLevels export (#14765) +- feat(core)!: Stop accepting `event` as argument for `recordDroppedEvent` (#14999) +- feat(core)!: Stop setting user in `requestDataIntegration` (#14898) +- feat(core)!: Type sdkProcessingMetadata more strictly (#14855) +- feat(core)!: Update `hasTracingEnabled` to consider empty trace config (#14857) +- feat(core)!: Update `requestDataIntegration` handling (#14806) +- feat(deno)!: Remove deno prepack (#14829) +- feat(ember)!: Officially drop support for ember `<=3.x` (#15032) +- feat(nestjs)!: Move `nestIntegration` into nest sdk and remove `setupNestErrorHandler` (#14751) +- feat(nestjs)!: Remove `@WithSentry` decorator (#14762) +- feat(nestjs)!: Remove `SentryService` (#14759) +- feat(nextjs)!: Don't rely on Next.js Build ID for release names (#14939) +- feat(nextjs)!: Remove `experimental_captureRequestError` (#14607) +- feat(nextjs)!: Respect user-provided source map generation settings (#14956) +- feat(node)!: Add support for Prisma v6 and drop v5 support (#15120) +- feat(node)!: Avoid http spans by default for custom OTEL setups (#14678) +- feat(node)!: Collect request sessions via HTTP instrumentation (#14658) +- feat(node)!: Remove `processThreadBreadcrumbIntegration` (#14666) +- feat(node)!: Remove fine grained `registerEsmLoaderHooks` (#15002) +- feat(opentelemetry)!: Exclusively pass root spans through sampling pipeline (#14951) +- feat(pinia)!: Include state of all stores in breadcrumb (#15312) +- feat(react)!: Raise minimum supported TanStack Router version to `1.63.0` (#15030) +- feat(react)!: Remove deprecated `getNumberOfUrlSegments` method (#14744) +- feat(react)!: Remove deprecated react router methods (#14743) +- feat(react)!: Update `ErrorBoundary` `componentStack` type (#14742) +- feat(remix)!: Drop support for Remix v1 (#14988) +- feat(remix)!: Remove `autoInstrumentRemix` option (#15074) +- feat(solidstart)!: Default to `--import` setup and add `autoInjectServerSentry` (#14862) +- feat(solidstart)!: No longer export `sentrySolidStartVite` (#15143) +- feat(solidstart)!: Respect user-provided source map setting (#14979) +- feat(svelte)!: Disable component update tracking by default (#15265) +- feat(sveltekit)!: Drop support for SvelteKit @1.x (#15037) +- feat(sveltekit)!: Remove `fetchProxyScriptNonce` option (#15123) +- feat(sveltekit)!: Respect user-provided source map generation settings (#14886) +- feat(utils)!: Remove `@sentry/utils` package (#14830) +- feat(vue)!: Remove configuring Vue tracing options anywhere else other than through the `vueIntegration`'s `tracingOptions` option (#14856) +- feat(vue/nuxt)!: No longer create `"update"` spans for component tracking by default (#14602) +- fix(node)!: Fix name of `vercelAIIntegration` to `VercelAI` (#15298) +- fix(vue)!: Remove `logError` from `vueIntegration` (#14958) +- ref!: Don't polyfill optional chaining and nullish coalescing (#14603) +- ref(core)!: Cleanup internal types, including `ReportDialogOptions` (#14861) +- ref(core)!: Mark exceptions from `captureConsoleIntegration` as `handled: true` by default (#14734) +- ref(core)!: Move `shutdownTimeout` option type from core to node (#15217) +- ref(core)!: Remove `Scope` type interface in favor of using `Scope` class (#14721) +- ref(core)!: Remove backwards compatible SentryCarrier type (#14697) + +### Other Changes + +- chore(browser): Export ipAddress helpers for use in other SDKs (#15079) +- deps(node): Bump `import-in-the-middle` to `1.12.0` (#14796) +- feat(aws): Rename AWS lambda layer name to `SentryNodeServerlessSDKv9` (#14927) +- feat(aws-serverless): Upgrade OTEL deps (#15091) +- feat(browser): Set `user.ip_address` explicitly to `{{auto}}` (#15008) +- feat(core): Add `inheritOrSampleWith` helper to `traceSampler` (#15277) +- feat(core): Emit client reports for unsampled root spans on span start (#14936) +- feat(core): Rename `hasTracingEnabled` to `hasSpansEnabled` (#15309) +- feat(core): Streamline `SpanJSON` type (#14693) +- feat(deno): Don't bundle `@sentry/deno` (#15014) +- feat(deno): Don't publish to `deno.land` (#15016) +- feat(deno): Stop inlining types from core (#14729) +- feat(deps): Bump @opentelemetry/instrumentation-amqplib from 0.45.0 to 0.46.0 (#14835) +- feat(deps): Bump @opentelemetry/instrumentation-aws-lambda from 0.49.0 to 0.50.0 (#14833) +- feat(deps): Bump @opentelemetry/instrumentation-express from 0.46.0 to 0.47.0 (#14834) +- feat(deps): Bump @opentelemetry/instrumentation-mysql2 from 0.44.0 to 0.45.0 (#14836) +- feat(deps): Bump @opentelemetry/propagation-utils from 0.30.14 to 0.30.15 (#14832) +- feat(deps): bump @opentelemetry/context-async-hooks from 1.29.0 to 1.30.0 (#14869) +- feat(deps): bump @opentelemetry/instrumentation-generic-pool from 0.42.0 to 0.43.0 (#14870) +- feat(deps): bump @opentelemetry/instrumentation-knex from 0.43.0 to 0.44.0 (#14872) +- feat(deps): bump @opentelemetry/instrumentation-mongodb from 0.50.0 to 0.51.0 (#14871) +- feat(deps): bump @opentelemetry/instrumentation-tedious from 0.17.0 to 0.18.0 (#14868) +- feat(deps): bump @sentry/cli from 2.39.1 to 2.41.1 (#15173) +- feat(flags): Add Statsig browser integration (#15319) +- feat(gatsby): Preserve user-provided source map settings (#15006) +- feat(nestjs): Remove `SentryTracingInterceptor`, `SentryGlobalGraphQLFilter`, `SentryGlobalGenericFilter` (#14761) +- feat(nextjs): Directly forward `sourcemaps.disable` to webpack plugin (#15109) +- feat(node): Add `processSessionIntegration` (#15081) +- feat(node): Add missing `vercelAIIntegration` export (#15318) +- feat(node): Capture exceptions from `worker_threads` (#15105) +- feat(nuxt): Add enabled to disable Sentry module (#15337) +- feat(nuxt): add `silent`, `errorHandler`, `release` to `SourceMapsOptions` (#15246) +- feat(profiling-node): Use `@sentry-internal/node-cpu-profiler` (#15208) +- feat(replay): Update fflate to 0.8.2 (#14867) +- feat(solidstart): Add `autoInjectServerSentry: 'experimental_dynamic-import` (#14863) +- feat(sveltekit): Only inject fetch proxy script for SvelteKit < 2.16.0 (#15126) +- feat(user feedback): Adds draw tool for UF screenshot annotations (#15062) +- feat(user feedback): Adds toolbar for cropping and annotating (#15282) +- feat: Avoid class fields all-together (#14887) +- feat: Only emit `__esModule` properties in CJS modules when there is a default export (#15018) +- feat: Pass `parentSampleRate` to `tracesSampler` (#15024) +- feat: Propagate and use a sampling random (#14989) +- fix(browser): Remove `browserPerformanceTimeOrigin` side-effects (#14025) +- fix(core): Ensure debugIds are applied to all exceptions in an event (#14881) +- fix(core): Fork scope if custom scope is passed to `startSpanManual` (#14901) +- fix(core): Fork scope if custom scope is passed to `startSpan` (#14900) +- fix(core): Only fall back to `sendDefaultPii` for IP collection in `requestDataIntegration` (#15125) +- fix(nextjs): Flush with `waitUntil` in `captureRequestError` (#15146) +- fix(nextjs): Use batched devserver symbolication endpoint (#15335) +- fix(node): Don't leak `__span` property into breadcrumbs (#14798) +- fix(node): Fix sample rand propagation for negative sampling decisions (#15045) +- fix(node): Missing `release` from ANR sessions (#15138) +- fix(node): Set the correct fallback URL fields for outgoing https requests if they are not defined (#15316) +- fix(nuxt): Detect Azure Function runtime for flushing with timeout (#15288) +- fix(react): From location can be undefined in Tanstack Router Instrumentation (#15235) +- fix(react): Import default for hoistNonReactStatics (#15238) +- fix(react): Support lazy-loaded routes and components. (#15039) +- fix(solidstart): Do not copy release-injection map file (#15302) +- ref(browser): Improve active span handling for `browserTracingIntegration` (#14959) +- ref(browser): Improve setting of propagation scope for navigation spans (#15108) +- ref(browser): Skip browser extension warning in non-debug builds (#15310) +- ref(browser): Update `supportsHistory` check & history usage (#14696) +- ref(core): Ensure non-recording root spans have frozen DSC (#14964) +- ref(core): Log debug message when capturing error events (#14701) +- ref(core): Move log message about invalid sample rate (#15215) +- ref(node): Streamline check for adding performance integrations (#15021) +- ref(react): Adapt tanstack router type (#15241) +- ref(svelte): Remove SvelteKit detection (#15313) +- ref(sveltekit): Clean up sub-request check (#15251) + +Work in this release was contributed by @aloisklink, @arturovt, @aryanvdesh, @benjick, @chris-basebone, @davidturissini, @GrizliK1988, @jahands, @jrandolf, @kunal-511, @maximepvrt, @maxmaxme, @mstrokin, @nathankleyn, @nwalters512, @tannerlinsley, @tjhiggins, and @Zen-cronic. Thank you for your contributions! + +## 9.0.0-alpha.2 + +This is an alpha release of the upcoming major release of version 9. +This release does not yet entail a comprehensive changelog as version 9 is not yet stable. + +For this release's iteration of the migration guide, see the [Migration Guide as per `9.0.0-alpha.2`](https://github.com/getsentry/sentry-javascript/blob/fbedd59954d378264d11b879b6eb2a482fbc0d1b/MIGRATION.md). +Please note that the migration guide is work in progress and subject to change. + +## 9.0.0-alpha.1 + +This is an alpha release of the upcoming major release of version 9. +This release does not yet entail a comprehensive changelog as version 9 is not yet stable. + +For this release's iteration of the migration guide, see the [Migration Guide as per `9.0.0-alpha.1`](https://github.com/getsentry/sentry-javascript/blob/e4333e5ce2d65be319ee6a5a5976f7c93983a417/docs/migration/v8-to-v9.md). +Please note that the migration guide is work in progress and subject to change. + +## 9.0.0-alpha.0 + +This is an alpha release of the upcoming major release of version 9. +This release does not yet entail a comprehensive changelog as version 9 is not yet stable. + +For this release's iteration of the migration guide, see the [Migration Guide as per `9.0.0-alpha.0`](https://github.com/getsentry/sentry-javascript/blob/6e4b593adcc4ce951afa8ae0cda0605ecd226cda/docs/migration/v8-to-v9.md). +Please note that the migration guide is work in progress and subject to change. + +## 8.x + +A full list of changes in the `8.x` release of the SDK can be found in the [8.x Changelog](./docs/changelog/v8.md). + +## 7.x + +A full list of changes in the `7.x` release of the SDK can be found in the [7.x Changelog](./docs/changelog/v7.md). + +## 6.x + +A full list of changes in the `6.x` release of the SDK can be found in the [6.x Changelog](./docs/changelog/v6.md). + +## 5.x + +A full list of changes in the `5.x` release of the SDK can be found in the [5.x Changelog](./docs/changelog/v5.md). + +## 4.x + +A full list of changes in the `4.x` release of the SDK can be found in the [4.x Changelog](./docs/changelog/v4.md). diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000000..e515c171303e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,151 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +# SDK Development Rules + +You are working on the Sentry JavaScript SDK, a critical production SDK used by thousands of applications. Follow these rules strictly. + +## Code Quality Requirements (MANDATORY) + +**CRITICAL**: All changes must pass these checks before committing: + +1. **Always run `yarn lint`** - Fix all linting issues +2. **Always run `yarn test`** - Ensure all tests pass +3. **Always run `yarn build:dev`** - Verify TypeScript compilation + +## Development Commands + +### Build Commands + +- `yarn build` - Full production build with package verification +- `yarn build:dev` - Development build (transpile + types) +- `yarn build:dev:watch` - Development build in watch mode (recommended) +- `yarn build:dev:filter ` - Build specific package and dependencies +- `yarn build:types:watch` - Watch mode for TypeScript types only +- `yarn build:bundle` - Build browser bundles only + +### Testing + +- `yarn test` - Run all unit tests + +### Linting and Formatting + +- `yarn lint` - Run ESLint and Prettier checks +- `yarn fix` - Auto-fix linting and formatting issues +- `yarn lint:es-compatibility` - Check ES compatibility + +## Git Flow Branching Strategy + +This repository uses **Git Flow**. See [docs/gitflow.md](docs/gitflow.md) for details. + +### Key Rules + +- **All PRs target `develop` branch** (NOT `master`) +- `master` represents the last released state +- Never merge directly into `master` (except emergency fixes) +- Avoid changing `package.json` files on `develop` during pending releases +- Never update dependencies, package.json content or build scripts unless explicitly asked for +- When asked to do a task on a set of files, always make sure that all occurences in the codebase are covered. Double check that no files have been forgotten. +- Unless explicitly asked for, make sure to cover all files, including files in `src/` and `test/` directories. + +### Branch Naming + +- Features: `feat/descriptive-name` +- Releases: `release/X.Y.Z` + +## Repository Architecture + +This is a Lerna monorepo with 40+ packages in the `@sentry/*` namespace. + +### Core Packages + +- `packages/core/` - Base SDK with interfaces, type definitions, core functionality +- `packages/types/` - Shared TypeScript type definitions - this is deprecated, never modify this package +- `packages/browser-utils/` - Browser-specific utilities and instrumentation +- `packages/node-core/` - Node Core SDK which contains most of the node-specific logic, excluding OpenTelemetry instrumentation. + +### Platform SDKs + +- `packages/browser/` - Browser SDK with bundled variants +- `packages/node/` - Node.js SDK. All general Node code should go into node-core, the node package itself only contains OpenTelemetry instrumentation on top of that. +- `packages/bun/`, `packages/deno/`, `packages/cloudflare/` - Runtime-specific SDKs + +### Framework Integrations + +- Framework packages: `packages/{framework}/` (react, vue, angular, etc.) +- Client/server entry points where applicable (nextjs, nuxt, sveltekit) +- Integration tests use Playwright (Remix, browser-integration-tests) + +### User Experience Packages + +- `packages/replay-internal/` - Session replay functionality +- `packages/replay-canvas/` - Canvas recording for replay +- `packages/replay-worker/` - Web worker support for replay +- `packages/feedback/` - User feedback integration + +### Development Packages (`dev-packages/`) + +- `browser-integration-tests/` - Playwright browser tests +- `e2e-tests/` - End-to-end tests for 70+ framework combinations +- `node-integration-tests/` - Node.js integration tests +- `test-utils/` - Shared testing utilities +- `bundle-analyzer-scenarios/` - Bundle analysis +- `rollup-utils/` - Build utilities +- GitHub Actions packages for CI/CD automation + +## Development Guidelines + +### Build System + +- Uses Rollup for bundling (`rollup.*.config.mjs`) +- TypeScript with multiple tsconfig files per package +- Lerna manages package dependencies and publishing +- Vite for testing with `vitest` + +### Package Structure Pattern + +Each package typically contains: + +- `src/index.ts` - Main entry point +- `src/sdk.ts` - SDK initialization logic +- `rollup.npm.config.mjs` - Build configuration +- `tsconfig.json`, `tsconfig.test.json`, `tsconfig.types.json` +- `test/` directory with corresponding test files + +### Key Development Notes + +- Uses Volta for Node.js/Yarn version management +- Requires initial `yarn build` after `yarn install` for TypeScript linking +- Integration tests use Playwright extensively +- Never change the volta, yarn, or package manager setup in general unless explicitly asked for + +### Notes for Background Tasks + +- Make sure to use [volta](https://volta.sh/) for development. Volta is used to manage the node, yarn and pnpm version used. +- Make sure that [PNPM support is enabled in volta](https://docs.volta.sh/advanced/pnpm). This means that the `VOLTA_FEATURE_PNPM` environment variable has to be set to `1`. +- Yarn, Node and PNPM have to be used through volta, in the versions defined by the volta config. NEVER change any versions unless explicitly asked to. + +## Testing Single Packages + +- Test specific package: `cd packages/{package-name} && yarn test` +- Build specific package: `yarn build:dev:filter @sentry/{package-name}` + +## Code Style Rules + +- Follow existing code conventions in each package +- Check imports and dependencies - only use libraries already in the codebase +- Look at neighboring files for patterns and style +- Never introduce code that exposes secrets or keys +- Follow security best practices + +## Before Every Commit Checklist + +1. ✅ `yarn lint` (fix all issues) +2. ✅ `yarn test` (all tests pass) +3. ✅ `yarn build:dev` (builds successfully) +4. ✅ Target `develop` branch for PRs (not `master`) + +## Documentation Sync + +**IMPORTANT**: When editing CLAUDE.md, also update .cursor/rules/sdk_development.mdc and vice versa to keep both files in sync. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac065621e10b..8d486d6718c1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,97 +1,123 @@ +

+ + Sentry + +

+ # Contributing +We welcome suggested improvements and bug fixes to the `@sentry/*` family of packages, in the form of pull requests on +[`GitHub`](https://github.com/getsentry/sentry-javascript). The guide below will help you get started, but if you have +further questions, please feel free to reach out on [Discord](https://discord.gg/Ww9hbqr). To learn about some general +SDK development principles check out the [SDK Development Guide](https://develop.sentry.dev/sdk/) in the Sentry +Developer Documentation. + ## Setting up an Environment -To run the test suite and run our code linter, node.js and npm are required. If you don't have node installed, [get it here](http://nodejs.org/download/) first. +We use [Volta](https://volta.sh/) to ensure we use consistent versions of node, yarn and pnpm. + +Make sure to also enable [pnpm support in Volta](https://docs.volta.sh/advanced/pnpm) if you want to run the E2E tests +locally. -Installing all other dependencies is as simple as: +`sentry-javascript` is a monorepo containing several packages, and we use `lerna` to manage them. To get started, +install all dependencies, and then perform an initial build, so TypeScript can read all of the linked type definitions. -```bash -$ npm install +``` +$ yarn +$ yarn build ``` -And if you don't have [Grunt](http://gruntjs.com/) already, feel free to install that globally: +With that, the repo is fully set up and you are ready to run all commands. -```bash -$ npm install -g grunt-cli -``` +## Building Packages -## Running the Test Suite +Since we are using [`TypeScript`](https://www.typescriptlang.org/), you need to transpile the code to JavaScript to be +able to use it. From the top level of the repo, there are three commands available: -The test suite is powered by [Mocha](http://visionmedia.github.com/mocha/) and can both run from the command line or in the browser. +- `yarn build:dev`, which runs a one-time build of every package +- `yarn build:dev:filter `, which runs `yarn build:dev` only in projects relevant to the given + package (so, for example, running `yarn build:dev:filter @sentry/react` will build the `react` package, all of its + dependencies (`utils`, `core`, `browser`, etc), and all packages which depend on it (currently `gatsby` and `nextjs`)) +- `yarn build:dev:watch`, which runs `yarn build:dev` in watch mode (recommended) -From the command line: +Note: Due to package incompatibilities between Python versions, building native binaries currently requires a Python +version <3.12. -```bash -$ grunt test -``` +You can also run a production build via `yarn build`, which will build everything except for the tarballs for publishing +to NPM. You can use this if you want to bundle Sentry yourself. The build output can be found in the packages `build/` +folder, e.g. `packages/browser/build`. Bundled files can be found in `packages/browser/build/bundles`. Note that there +are no guarantees about the produced file names etc., so make sure to double check which files are generated after +upgrading. -From your browser: +## Testing SDK Packages Locally -```bash -$ grunt run:test -``` +To test local versions of SDK packages, for instance in test projects, you have a couple of options: -Then visit: http://localhost:8000/test/ +- Use [`yarn link`](https://classic.yarnpkg.com/lang/en/docs/cli/link/) to symlink your package to the test project. +- Use [`yalc` to install SDK packages](./docs/using-yalc.md) as if they were already published. +- Run `build:tarball` in the repo and `yarn add ./path/to/tarball.tgz` in the project. -## Compiling Raven.js +## Adding Tests -The simplest way to compile your own version of Raven.js is with the supplied grunt command: +**Any nontrivial fixes/features should include tests.** You'll find a `test` folder in each package. -```bash -$ grunt build -``` +For browser related changes, you may also add tests in `dev-packages/browser-integration-tests`. Similarly, for node +integration tests can be added in `dev-packages/node-integration-tests`. Finally, we also have E2E test apps in +`dev-packages/e2e-tests`. -By default, this will compile raven.js and all of the included plugins. +## Running Tests -If you only want to compile the core raven.js: +Running tests works the same way as building - running `yarn test` at the project root will run tests for all packages, +and running `yarn test` in a specific package will run tests for that package. There are also commands to run subsets of +the tests in each location. Check out the `scripts` entry of the corresponding `package.json` for details. -```bash -$ grunt build.core -``` +Note: you must run `yarn build` before `yarn test` will work. -Files are compiled into `build/`. +## Debug Build Flags -## Contributing Back Code +Throughout the codebase, you will find a `__DEBUG_BUILD__` constant. This flag serves two purposes: -Please, send over suggestions and bug fixes in the form of pull requests on [GitHub](https://github.com/getsentry/raven-js). Any nontrivial fixes/features should include tests. -Do not include any changes to the `dist/` folder or bump version numbers yourself. +1. It enables us to remove debug code from our minified CDN bundles during build, by replacing the flag with `false` + before tree-shaking occurs. +2. It enables users to remove Sentry debug code from their production bundles during their own build. When we build our + npm packages, we replace the flag with `(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__)`. If the user + does nothing, this evaluates to `true` and logging is included. But if the user runs their own replacement during + build (again replacing the flag with `false`), the build will tree-shake the logging away, just as our bundle builds + do. -## Documentation +Note that the replacement flag, `__SENTRY_DEBUG__`, is different from the original flag . This is necessary because the +replacement plugin runs twice, at two different stages of the build, and we don't want to run a replacement on our +replacement (as would happen if we reused `__DEBUG_BUILD__`). -The documentation is written using [reStructuredText](http://en.wikipedia.org/wiki/ReStructuredText), and compiled using [Sphinx](http://sphinx-doc.org/). If you don't have Sphinx installed, you can do it using following command (assuming you have Python already installed in your system): +## Linting -```bash -$ pip install sphinx -``` +Similar to building and testing, linting can be done in the project root or in individual packages by calling +`yarn lint`. -Documentation can be then compiled by running: +Note: you must run `yarn build` before `yarn lint` will work. -```bash -$ make docs -``` +## External Contributors -Afterwards you can view it in your browser by running following command and than pointing your browser to http://127.0.0.1:8000/: +We highly appreciate external contributions to the SDK. If you want to contribute something, you can just open a PR +against `develop`. -```bash -$ grunt run:docs -``` +The SDK team will check out your PR shortly! + +When contributing to the codebase, please note: + +- Make sure to follow the [Commit, Issue & PR guidelines](./docs/commit-issue-pr-guidelines.md) +- Non-trivial PRs will not be accepted without tests (see above). + +## Commit, Issue & PR guidelines + +See [Commit, Issue & PR guidelines](./docs/commit-issue-pr-guidelines.md). + +## PR Reviews + +See [PR Reviews](./docs/pr-reviews.md). + +## Publishing a Release + +_These steps are only relevant to Sentry employees when preparing and publishing a new SDK release._ -## Releasing New Version - -* Bump version numbers in both `package.json` and `bower.json`. -* Bump version across all docs under `docs/` -* Put together [CHANGELOG](https://github.com/getsentry/raven-js/blob/master/CHANGELOG.md) -* `$ grunt dist` This will compile a new version and update it in the `dist/` folder. -* Confirm that build was fine, etc. -* Commit new version, create a tag. Push to GitHub. -* Copy CHANGELOG entry into a new GH Release: https://github.com/getsentry/raven-js/releases -* `$ grunt publish` to recompile all plugins and all permutations and upload to S3. -* `$ npm publish` to push to npm. -* Confirm that the new version exists behind `cdn.ravenjs.com` -* Bump version in the `gh-pages` branch specifically for http://ravenjs.com/. -* Bump marketing pages on getsentry.com, e.g. https://getsentry.com/for/javascript/ -* Bump getsentry.com ` + + + diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/keepSentryGlobal/test.ts b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/keepSentryGlobal/test.ts new file mode 100644 index 000000000000..7b04ff8a22eb --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/keepSentryGlobal/test.ts @@ -0,0 +1,15 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../../utils/helpers'; + +sentryTest('keeps data on window.Sentry intact', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForErrorRequestOnUrl(page, url); + + const eventData = envelopeRequestParser(req); + + expect(eventData.message).toBe('Test exception'); + + const customThingy = await page.evaluate('window.Sentry._customThingOnSentry'); + expect(customThingy).toBe('customThingOnSentry'); +}); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/onLoadLate/init.js b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/onLoadLate/init.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/onLoadLate/subject.js b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/onLoadLate/subject.js new file mode 100644 index 000000000000..1dcef58798cc --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/onLoadLate/subject.js @@ -0,0 +1,7 @@ +Sentry.forceLoad(); + +setTimeout(() => { + Sentry.onLoad(function () { + Sentry.captureException('Test exception'); + }); +}, 200); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/onLoadLate/test.ts b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/onLoadLate/test.ts new file mode 100644 index 000000000000..cba21fe45154 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/onLoadLate/test.ts @@ -0,0 +1,12 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../../utils/helpers'; + +sentryTest('late onLoad call is handled', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForErrorRequestOnUrl(page, url); + + const eventData = envelopeRequestParser(req); + + expect(eventData.message).toBe('Test exception'); +}); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/pageloadTransaction/init.js b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/pageloadTransaction/init.js new file mode 100644 index 000000000000..7c0fceed58a4 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/pageloadTransaction/init.js @@ -0,0 +1,5 @@ +window._testBaseTimestamp = performance.timeOrigin / 1000; + +Sentry.onLoad(function () { + Sentry.init({}); +}); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/pageloadTransaction/test.ts b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/pageloadTransaction/test.ts new file mode 100644 index 000000000000..257934434358 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/pageloadTransaction/test.ts @@ -0,0 +1,27 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import { + envelopeRequestParser, + shouldSkipTracingTest, + waitForTransactionRequestOnUrl, +} from '../../../../utils/helpers'; + +sentryTest('should create a pageload transaction', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForTransactionRequestOnUrl(page, url); + + const eventData = envelopeRequestParser(req); + const timeOrigin = await page.evaluate('window._testBaseTimestamp'); + + const { start_timestamp: startTimestamp } = eventData; + + expect(startTimestamp).toBeCloseTo(timeOrigin, 1); + + expect(eventData.contexts?.trace?.op).toBe('pageload'); + expect(eventData.spans?.length).toBeGreaterThan(0); + expect(eventData.transaction_info?.source).toEqual('url'); +}); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/replay/init.js b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/replay/init.js new file mode 100644 index 000000000000..e55a8aefdc0b --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/replay/init.js @@ -0,0 +1,5 @@ +Sentry.onLoad(function () { + Sentry.init({ + replaysSessionSampleRate: 1, + }); +}); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/replay/test.ts b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/replay/test.ts new file mode 100644 index 000000000000..d8b00740d412 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/replay/test.ts @@ -0,0 +1,19 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import { getReplayEvent, shouldSkipReplayTest, waitForReplayRequest } from '../../../../utils/replayHelpers'; + +sentryTest('should capture a replay', async ({ getLocalTestUrl, page }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + const req = waitForReplayRequest(page); + + const url = await getLocalTestUrl({ testDir: __dirname }); + await page.goto(url); + + const eventData = getReplayEvent(await req); + + expect(eventData).toBeDefined(); + expect(eventData.segment_id).toBe(0); +}); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoad/init.js b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoad/init.js new file mode 100644 index 000000000000..e599fa75bd0a --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoad/init.js @@ -0,0 +1 @@ +// we define sentryOnLoad in template diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoad/subject.js b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoad/subject.js new file mode 100644 index 000000000000..fb0796f7f299 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoad/subject.js @@ -0,0 +1 @@ +Sentry.captureException('Test exception'); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoad/template.html b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoad/template.html new file mode 100644 index 000000000000..ed23488f1bf3 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoad/template.html @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoad/test.ts b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoad/test.ts new file mode 100644 index 000000000000..b44cd9ac6e35 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoad/test.ts @@ -0,0 +1,14 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../../utils/helpers'; + +sentryTest('sentryOnLoad callback is called before Sentry.onLoad()', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForErrorRequestOnUrl(page, url); + + const eventData = envelopeRequestParser(req); + + expect(eventData.message).toBe('Test exception'); + + expect(await page.evaluate('Sentry.getClient().getOptions().tracesSampleRate')).toEqual(0.123); +}); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadAndOnLoad/init.js b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadAndOnLoad/init.js new file mode 100644 index 000000000000..422f4ca103df --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadAndOnLoad/init.js @@ -0,0 +1,4 @@ +Sentry.onLoad(function () { + // this should be called _after_ window.sentryOnLoad + Sentry.captureException(`Test exception: ${Sentry.getClient().getOptions().tracesSampleRate}`); +}); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadAndOnLoad/subject.js b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadAndOnLoad/subject.js new file mode 100644 index 000000000000..fb0796f7f299 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadAndOnLoad/subject.js @@ -0,0 +1 @@ +Sentry.captureException('Test exception'); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadAndOnLoad/template.html b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadAndOnLoad/template.html new file mode 100644 index 000000000000..ed23488f1bf3 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadAndOnLoad/template.html @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadAndOnLoad/test.ts b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadAndOnLoad/test.ts new file mode 100644 index 000000000000..8658bf5f21a3 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadAndOnLoad/test.ts @@ -0,0 +1,14 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../../utils/helpers'; + +sentryTest('sentryOnLoad callback is used', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForErrorRequestOnUrl(page, url); + + const eventData = envelopeRequestParser(req); + + expect(eventData.message).toBe('Test exception: 0.123'); + + expect(await page.evaluate('Sentry.getClient().getOptions().tracesSampleRate')).toEqual(0.123); +}); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadError/init.js b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadError/init.js new file mode 100644 index 000000000000..e599fa75bd0a --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadError/init.js @@ -0,0 +1 @@ +// we define sentryOnLoad in template diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadError/subject.js b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadError/subject.js new file mode 100644 index 000000000000..fb0796f7f299 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadError/subject.js @@ -0,0 +1 @@ +Sentry.captureException('Test exception'); diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadError/template.html b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadError/template.html new file mode 100644 index 000000000000..621440305e47 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadError/template.html @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadError/test.ts b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadError/test.ts new file mode 100644 index 000000000000..4adffb2865d2 --- /dev/null +++ b/dev-packages/browser-integration-tests/loader-suites/loader/onLoad/sentryOnLoadError/test.ts @@ -0,0 +1,30 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../../utils/helpers'; + +sentryTest( + 'sentryOnLoad callback is called before Sentry.onLoad() and handles errors in handler', + async ({ getLocalTestUrl, page }) => { + const errors: string[] = []; + + page.on('console', msg => { + if (msg.type() === 'error') { + errors.push(msg.text()); + } + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForErrorRequestOnUrl(page, url); + + const eventData = envelopeRequestParser(req); + + expect(eventData.message).toBe('Test exception'); + + expect(await page.evaluate('Sentry.getClient().getOptions().tracesSampleRate')).toEqual(0.123); + + expect(errors).toEqual([ + 'Error while calling `sentryOnLoad` handler:', + expect.stringContaining('Error: sentryOnLoad error'), + ]); + }, +); diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json new file mode 100644 index 000000000000..329c061c49d5 --- /dev/null +++ b/dev-packages/browser-integration-tests/package.json @@ -0,0 +1,62 @@ +{ + "name": "@sentry-internal/browser-integration-tests", + "version": "10.19.0", + "main": "index.js", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "private": true, + "scripts": { + "clean": "rimraf -g suites/**/dist loader-suites/**/dist tmp", + "install-browsers": "[[ -z \"$SKIP_PLAYWRIGHT_BROWSER_INSTALL\" ]] && npx playwright install --with-deps || echo 'Skipping browser installation'", + "lint": "eslint . --format stylish", + "fix": "eslint . --format stylish --fix", + "type-check": "tsc", + "postinstall": "yarn install-browsers", + "pretest": "yarn clean && yarn type-check", + "test": "yarn test:all --project='chromium'", + "test:all": "npx playwright test -c playwright.browser.config.ts", + "test:bundle": "PW_BUNDLE=bundle yarn test", + "test:bundle:min": "PW_BUNDLE=bundle_min yarn test", + "test:bundle:replay": "PW_BUNDLE=bundle_replay yarn test", + "test:bundle:replay:min": "PW_BUNDLE=bundle_replay_min yarn test", + "test:bundle:tracing": "PW_BUNDLE=bundle_tracing yarn test", + "test:bundle:tracing:min": "PW_BUNDLE=bundle_tracing_min yarn test", + "test:bundle:full": "PW_BUNDLE=bundle_tracing_replay_feedback yarn test", + "test:bundle:full:min": "PW_BUNDLE=bundle_tracing_replay_feedback_min yarn test", + "test:cjs": "PW_BUNDLE=cjs yarn test", + "test:esm": "PW_BUNDLE=esm yarn test", + "test:loader": "npx playwright test -c playwright.loader.config.ts --project='chromium'", + "test:loader:base": "PW_BUNDLE=loader_base yarn test:loader", + "test:loader:eager": "PW_BUNDLE=loader_eager yarn test:loader", + "test:loader:tracing": "PW_BUNDLE=loader_tracing yarn test:loader", + "test:loader:replay": "PW_BUNDLE=loader_replay yarn test:loader", + "test:loader:replay_buffer": "PW_BUNDLE=loader_replay_buffer yarn test:loader", + "test:loader:full": "PW_BUNDLE=loader_tracing_replay yarn test:loader", + "test:loader:debug": "PW_BUNDLE=loader_debug yarn test:loader", + "test:update-snapshots": "yarn test:all --update-snapshots", + "test:detect-flaky": "ts-node scripts/detectFlakyTests.ts" + }, + "dependencies": { + "@babel/core": "^7.27.7", + "@babel/preset-typescript": "^7.16.7", + "@playwright/test": "~1.53.2", + "@sentry-internal/rrweb": "2.34.0", + "@sentry/browser": "10.19.0", + "@supabase/supabase-js": "2.49.3", + "axios": "^1.12.2", + "babel-loader": "^8.2.2", + "fflate": "0.8.2", + "html-webpack-plugin": "^5.5.0", + "webpack": "^5.95.0" + }, + "devDependencies": { + "@types/glob": "8.0.0", + "@types/node": "^18.19.1", + "glob": "8.0.3" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/browser-integration-tests/playwright.browser.config.ts b/dev-packages/browser-integration-tests/playwright.browser.config.ts new file mode 100644 index 000000000000..dd48c8f54746 --- /dev/null +++ b/dev-packages/browser-integration-tests/playwright.browser.config.ts @@ -0,0 +1,9 @@ +import type { PlaywrightTestConfig } from '@playwright/test'; +import CorePlaywrightConfig from './playwright.config'; + +const config: PlaywrightTestConfig = { + ...CorePlaywrightConfig, + testDir: './suites', +}; + +export default config; diff --git a/dev-packages/browser-integration-tests/playwright.config.ts b/dev-packages/browser-integration-tests/playwright.config.ts new file mode 100644 index 000000000000..821c0291ccfb --- /dev/null +++ b/dev-packages/browser-integration-tests/playwright.config.ts @@ -0,0 +1,39 @@ +import type { PlaywrightTestConfig } from '@playwright/test'; +import { devices } from '@playwright/test'; + +const config: PlaywrightTestConfig = { + retries: 0, + // Run tests inside of a single file in parallel + fullyParallel: true, + // Use 3 workers on CI, else use defaults (based on available CPU cores) + // Note that 3 is a random number selected to work well with our CI setup + workers: process.env.CI ? 3 : undefined, + testMatch: /test.ts/, + + use: { + trace: 'retain-on-failure', + }, + + projects: [ + { + name: 'chromium', + use: devices['Desktop Chrome'], + }, + { + name: 'webkit', + use: devices['Desktop Safari'], + }, + { + name: 'firefox', + grep: /@firefox/i, + use: devices['Desktop Firefox'], + }, + ], + + reporter: process.env.CI ? [['list'], ['junit', { outputFile: 'results.junit.xml' }]] : 'list', + + globalSetup: require.resolve('./playwright.setup.ts'), + globalTeardown: require.resolve('./playwright.teardown.ts'), +}; + +export default config; diff --git a/dev-packages/browser-integration-tests/playwright.loader.config.ts b/dev-packages/browser-integration-tests/playwright.loader.config.ts new file mode 100644 index 000000000000..01a829fbba3a --- /dev/null +++ b/dev-packages/browser-integration-tests/playwright.loader.config.ts @@ -0,0 +1,9 @@ +import type { PlaywrightTestConfig } from '@playwright/test'; +import CorePlaywrightConfig from './playwright.config'; + +const config: PlaywrightTestConfig = { + ...CorePlaywrightConfig, + testDir: './loader-suites', +}; + +export default config; diff --git a/dev-packages/browser-integration-tests/playwright.setup.ts b/dev-packages/browser-integration-tests/playwright.setup.ts new file mode 100644 index 000000000000..fe97c814199e --- /dev/null +++ b/dev-packages/browser-integration-tests/playwright.setup.ts @@ -0,0 +1,5 @@ +import setupStaticAssets from './utils/staticAssets'; + +export default function globalSetup(): Promise { + return setupStaticAssets(); +} diff --git a/dev-packages/browser-integration-tests/playwright.teardown.ts b/dev-packages/browser-integration-tests/playwright.teardown.ts new file mode 100644 index 000000000000..3afbf63f0aa1 --- /dev/null +++ b/dev-packages/browser-integration-tests/playwright.teardown.ts @@ -0,0 +1,5 @@ +import * as childProcess from 'child_process'; + +export default function globalTeardown(): void { + childProcess.execSync('yarn clean', { stdio: 'inherit', cwd: process.cwd() }); +} diff --git a/dev-packages/browser-integration-tests/scripts/detectFlakyTests.ts b/dev-packages/browser-integration-tests/scripts/detectFlakyTests.ts new file mode 100644 index 000000000000..8e0aadc6af59 --- /dev/null +++ b/dev-packages/browser-integration-tests/scripts/detectFlakyTests.ts @@ -0,0 +1,140 @@ +import * as childProcess from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as glob from 'glob'; + +/** + * Assume that each test runs for 3s. + */ +const ASSUMED_TEST_DURATION_SECONDS = 3; + +/** + * We keep the runtime of the detector if possible under 30min. + */ +const MAX_TARGET_TEST_RUNTIME_SECONDS = 30 * 60; + +/** + * Running one test 50x is what we consider enough to detect flakiness. + * Running one test 5x is the bare minimum + */ +const MAX_PER_TEST_RUN_COUNT = 50; +const MIN_PER_TEST_RUN_COUNT = 5; + +async function run(): Promise { + let testPaths: string[] = []; + + const changedPaths: string[] = process.env.CHANGED_TEST_PATHS ? JSON.parse(process.env.CHANGED_TEST_PATHS) : []; + + if (changedPaths.length > 0) { + console.log(`Detected changed test paths: +${changedPaths.join('\n')} + +`); + + testPaths = getTestPaths().filter(p => changedPaths.some(changedPath => changedPath.includes(p))); + if (testPaths.length === 0) { + console.log('Could not find matching tests, aborting...'); + process.exit(1); + } + } + + const repeatEachCount = getPerTestRunCount(testPaths); + console.log(`Running tests ${repeatEachCount} times each.`); + + const cwd = path.join(__dirname, '../'); + + try { + await new Promise((resolve, reject) => { + const cp = childProcess.spawn( + `npx playwright test ${ + testPaths.length ? testPaths.join(' ') : './suites' + } --repeat-each ${repeatEachCount} --project=chromium`, + { shell: true, cwd, stdio: 'inherit' }, + ); + + let error: Error | undefined; + + cp.on('error', e => { + console.error(e); + error = e; + }); + + cp.on('close', status => { + const err = error || (status !== 0 ? new Error(`Process exited with status ${status}`) : undefined); + + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + } catch (error) { + console.log(''); + console.log(''); + + console.error(`⚠️ Some tests failed.`); + console.error(error); + process.exit(1); + } + + console.log(''); + console.log(''); + console.log(`☑️ All tests passed.`); +} + +/** + * Returns how many time one test should run based on the chosen mode and a bunch of heuristics + */ +function getPerTestRunCount(testPaths: string[]) { + if ((!process.env.TEST_RUN_COUNT || process.env.TEST_RUN_COUNT === 'AUTO') && testPaths.length > 0) { + // Run everything up to 100x, assuming that total runtime is less than 60min. + // We assume an average runtime of 3s per test, times 4 (for different browsers) = 12s per detected testPaths + // We want to keep overall runtime under 30min + const estimatedNumberOfTests = testPaths.map(getApproximateNumberOfTests).reduce((a, b) => a + b); + console.log(`Estimated number of tests: ${estimatedNumberOfTests}`); + + const testRunCount = estimatedNumberOfTests; + console.log(`Estimated test runs for one round: ${testRunCount}`); + + const estimatedTestRuntime = testRunCount * ASSUMED_TEST_DURATION_SECONDS; + console.log(`Estimated test runtime: ${estimatedTestRuntime}s`); + + const expectedPerTestRunCount = Math.floor(MAX_TARGET_TEST_RUNTIME_SECONDS / estimatedTestRuntime); + console.log( + `Calculated # of repetitions: ${expectedPerTestRunCount} (min ${MIN_PER_TEST_RUN_COUNT}, max ${MAX_PER_TEST_RUN_COUNT})`, + ); + + return Math.min(MAX_PER_TEST_RUN_COUNT, Math.max(expectedPerTestRunCount, MIN_PER_TEST_RUN_COUNT)); + } + + return parseInt(process.env.TEST_RUN_COUNT || '5'); +} + +function getTestPaths(): string[] { + const paths = glob.sync('suites/**/test.{ts,js}', { + cwd: path.join(__dirname, '../'), + }); + + return paths.map(p => `${path.dirname(p)}/`); +} + +/** + * Definitely not bulletproof way of getting the number of tests in a file :D + * We simply match on `it(`, `test(`, etc and count the matches. + * + * Note: This test completely disregards parameterized tests (`it.each`, etc) or + * skipped/disabled tests and other edge cases. It's just a rough estimate. + */ +function getApproximateNumberOfTests(testPath: string): number { + try { + const content = fs.readFileSync(path.join(process.cwd(), testPath, 'test.ts'), 'utf-8'); + const matches = content.match(/sentryTest\(/g); + return Math.max(matches ? matches.length : 1, 1); + } catch { + console.error(`Could not read file ${testPath}`); + return 1; + } +} + +run(); diff --git a/dev-packages/browser-integration-tests/suites/errors/fetch/init.js b/dev-packages/browser-integration-tests/suites/errors/fetch/init.js new file mode 100644 index 000000000000..d8c94f36fdd0 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/errors/fetch/init.js @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', +}); diff --git a/dev-packages/browser-integration-tests/suites/errors/fetch/subject.js b/dev-packages/browser-integration-tests/suites/errors/fetch/subject.js new file mode 100644 index 000000000000..8bae73df7b31 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/errors/fetch/subject.js @@ -0,0 +1,45 @@ +// Based on possible TypeError exceptions from https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch + +// Network error (e.g. ad-blocked, offline, page does not exist, ...) +window.networkError = () => { + fetch('http://sentry-test-external.io/does-not-exist'); +}; + +window.networkErrorSubdomain = () => { + fetch('http://subdomain.sentry-test-external.io/does-not-exist'); +}; + +// Invalid header also produces TypeError +window.invalidHeaderName = () => { + fetch('http://sentry-test-external.io/invalid-header-name', { headers: { 'C ontent-Type': 'text/xml' } }); +}; + +// Invalid header value also produces TypeError +window.invalidHeaderValue = () => { + fetch('http://sentry-test-external.io/invalid-header-value', { headers: ['Content-Type', 'text/html', 'extra'] }); +}; + +// Invalid URL scheme +window.invalidUrlScheme = () => { + fetch('blub://sentry-test-external.io/invalid-scheme'); +}; + +// URL includes credentials +window.credentialsInUrl = () => { + fetch('https://user:password@sentry-test-external.io/credentials-in-url'); +}; + +// Invalid mode +window.invalidMode = () => { + fetch('https://sentry-test-external.io/invalid-mode', { mode: 'navigate' }); +}; + +// Invalid request method +window.invalidMethod = () => { + fetch('http://sentry-test-external.io/invalid-method', { method: 'CONNECT' }); +}; + +// No-cors mode with cors-required method +window.noCorsMethod = () => { + fetch('http://sentry-test-external.io/no-cors-method', { mode: 'no-cors', method: 'PUT' }); +}; diff --git a/dev-packages/browser-integration-tests/suites/errors/fetch/test.ts b/dev-packages/browser-integration-tests/suites/errors/fetch/test.ts new file mode 100644 index 000000000000..19fe923c7b30 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/errors/fetch/test.ts @@ -0,0 +1,287 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequest } from '../../../utils/helpers'; + +sentryTest('handles fetch network errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('networkError()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: 'Failed to fetch (sentry-test-external.io)', + webkit: 'Load failed (sentry-test-external.io)', + firefox: 'NetworkError when attempting to fetch resource. (sentry-test-external.io)', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'auto.browser.global_handlers.onunhandledrejection', + }, + }); +}); + +sentryTest('handles fetch network errors on subdomains @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('networkErrorSubdomain()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: 'Failed to fetch (subdomain.sentry-test-external.io)', + webkit: 'Load failed (subdomain.sentry-test-external.io)', + firefox: 'NetworkError when attempting to fetch resource. (subdomain.sentry-test-external.io)', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'auto.browser.global_handlers.onunhandledrejection', + }, + }); +}); + +sentryTest('handles fetch invalid header name errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('invalidHeaderName()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: "Failed to execute 'fetch' on 'Window': Invalid name", + webkit: "Invalid header name: 'C ontent-Type'", + firefox: 'Window.fetch: c ontent-type is an invalid header name.', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'auto.browser.global_handlers.onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); + +sentryTest('handles fetch invalid header value errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('invalidHeaderValue()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: + "Failed to execute 'fetch' on 'Window': Failed to read the 'headers' property from 'RequestInit': The provided value cannot be converted to a sequence.", + webkit: 'Value is not a sequence', + firefox: + "Window.fetch: Element of sequence> branch of (sequence> or record) can't be converted to a sequence.", + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'auto.browser.global_handlers.onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); + +sentryTest('handles fetch invalid URL scheme errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + await page.route('http://sentry-test-external.io/**', route => { + return route.fulfill({ + status: 200, + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('invalidUrlScheme()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: 'Failed to fetch (sentry-test-external.io)', + webkit: 'Load failed (sentry-test-external.io)', + firefox: 'NetworkError when attempting to fetch resource. (sentry-test-external.io)', + }; + + const error = errorMap[browserName]; + + /** + * This kind of error does show a helpful warning in the console, e.g.: + * Fetch API cannot load blub://sentry-test-external.io/invalid-scheme. URL scheme "blub" is not supported. + * But it seems we cannot really access this in the SDK :( + */ + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'auto.browser.global_handlers.onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); + +sentryTest('handles fetch credentials in url errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('credentialsInUrl()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: + "Failed to execute 'fetch' on 'Window': Request cannot be constructed from a URL that includes credentials: https://user:password@sentry-test-external.io/credentials-in-url", + webkit: 'URL is not valid or contains user credentials.', + firefox: + 'Window.fetch: https://user:password@sentry-test-external.io/credentials-in-url is an url with embedded credentials.', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'auto.browser.global_handlers.onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); + +sentryTest('handles fetch invalid mode errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('invalidMode()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: + "Failed to execute 'fetch' on 'Window': Cannot construct a Request with a RequestInit whose mode member is set as 'navigate'.", + webkit: 'Request constructor does not accept navigate fetch mode.', + firefox: 'Window.fetch: Invalid request mode navigate.', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'auto.browser.global_handlers.onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); + +sentryTest('handles fetch invalid request method errors @firefox', async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('invalidMethod()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: "Failed to execute 'fetch' on 'Window': 'CONNECT' HTTP method is unsupported.", + webkit: 'Method is forbidden.', + firefox: 'Window.fetch: Invalid request method CONNECT.', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'auto.browser.global_handlers.onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); + +sentryTest( + 'handles fetch no-cors mode with cors-required method errors @firefox', + async ({ getLocalTestUrl, page, browserName }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const reqPromise = waitForErrorRequest(page); + await page.goto(url); + await page.evaluate('noCorsMethod()'); + + const eventData = envelopeRequestParser(await reqPromise); + + const errorMap: Record = { + chromium: "Failed to execute 'fetch' on 'Window': 'PUT' is unsupported in no-cors mode.", + webkit: 'Method must be GET, POST or HEAD in no-cors mode.', + firefox: 'Window.fetch: Invalid request method PUT.', + }; + + const error = errorMap[browserName]; + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'TypeError', + value: error, + mechanism: { + handled: false, + type: 'auto.browser.global_handlers.onunhandledrejection', + }, + stacktrace: { + frames: expect.any(Array), + }, + }); + }, +); 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..740fb69558ed --- /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-button'); 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..d0c83c526ca4 --- /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..ffc00cc8258c --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/attachTo/test.ts @@ -0,0 +1,76 @@ +import { expect } from '@playwright/test'; +import { sentryTest, TEST_HOST } 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 { + return false; + } + }); + + const url = await getLocalTestUrl({ testDir: __dirname, handleLazyLoadedFeedback: true }); + + await page.goto(url); + await page.locator('#custom-feedback-button').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(), + settings: { + infer_ip: 'never', + }, + }, + request: { + url: `${TEST_HOST}/index.html`, + headers: { + 'User-Agent': expect.stringContaining(''), + }, + }, + platform: 'javascript', + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/init.js b/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/init.js new file mode 100644 index 000000000000..e6da8b5973bb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/init.js @@ -0,0 +1,14 @@ +import * as Sentry from '@sentry/browser'; +// Import this separately so that generatePlugin can handle it for CDN scenarios +import { feedbackIntegration } from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + feedbackIntegration({ + tags: { from: 'integration init' }, + }), + ], +}); diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/template.html b/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/template.html new file mode 100644 index 000000000000..57334d4ad2f1 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/template.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/test.ts b/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/test.ts new file mode 100644 index 000000000000..9d6cf1a8a1f1 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedback/test.ts @@ -0,0 +1,76 @@ +import { expect } from '@playwright/test'; +import { sentryTest, TEST_HOST } from '../../../utils/fixtures'; +import { envelopeRequestParser, getEnvelopeType, shouldSkipFeedbackTest } from '../../../utils/helpers'; + +sentryTest('should capture feedback', 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 { + return false; + } + }); + + const url = await getLocalTestUrl({ testDir: __dirname, handleLazyLoadedFeedback: true }); + + await page.goto(url); + await page.getByText('Report a Bug').click(); + 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', + tags: { + from: 'integration init', + }, + timestamp: expect.any(Number), + event_id: expect.stringMatching(/\w{32}/), + environment: 'production', + sdk: { + integrations: expect.arrayContaining(['Feedback']), + version: expect.any(String), + name: 'sentry.javascript.browser', + packages: expect.anything(), + settings: { + infer_ip: 'never', + }, + }, + request: { + url: `${TEST_HOST}/index.html`, + headers: { + 'User-Agent': expect.stringContaining(''), + }, + }, + platform: 'javascript', + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/hasSampling/init.js b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/hasSampling/init.js new file mode 100644 index 000000000000..c8d5dbcdb232 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/hasSampling/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'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + replaysOnErrorSampleRate: 1.0, + replaysSessionSampleRate: 1.0, + integrations: [ + Sentry.replayIntegration({ + flushMinDelay: 200, + flushMaxDelay: 200, + minReplayDuration: 0, + }), + feedbackIntegration(), + ], +}); diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/hasSampling/test.ts b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/hasSampling/test.ts new file mode 100644 index 000000000000..d76fd2089413 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/hasSampling/test.ts @@ -0,0 +1,110 @@ +import { expect } from '@playwright/test'; +import { sentryTest, TEST_HOST } from '../../../../utils/fixtures'; +import { envelopeRequestParser, getEnvelopeType, shouldSkipFeedbackTest } from '../../../../utils/helpers'; +import { + collectReplayRequests, + getReplayBreadcrumbs, + shouldSkipReplayTest, + waitForReplayRequest, +} from '../../../../utils/replayHelpers'; + +sentryTest('should capture feedback', async ({ forceFlushReplay, getLocalTestUrl, page }) => { + if (shouldSkipFeedbackTest() || shouldSkipReplayTest()) { + sentryTest.skip(); + } + + const reqPromise0 = waitForReplayRequest(page, 0); + + const feedbackRequestPromise = page.waitForResponse(res => { + const req = res.request(); + + const postData = req.postData(); + if (!postData) { + return false; + } + + try { + return getEnvelopeType(req) === 'feedback'; + } catch { + return false; + } + }); + + const url = await getLocalTestUrl({ testDir: __dirname, handleLazyLoadedFeedback: true }); + + await Promise.all([page.goto(url), page.getByText('Report a Bug').click(), reqPromise0]); + + const replayRequestPromise = collectReplayRequests(page, recordingEvents => { + return getReplayBreadcrumbs(recordingEvents).some(breadcrumb => breadcrumb.category === 'sentry.feedback'); + }); + + // Inputs are slow, these need to be serial + 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'); + + // Force flush here, as inputs are slow and can cause click event to be in unpredictable segments + await Promise.all([forceFlushReplay()]); + + const [, feedbackResp] = await Promise.all([ + page.locator('[data-sentry-feedback] .btn--primary').click(), + feedbackRequestPromise, + ]); + + const { replayEvents, replayRecordingSnapshots } = await replayRequestPromise; + const breadcrumbs = getReplayBreadcrumbs(replayRecordingSnapshots); + + const replayEvent = replayEvents[0]; + const feedbackEvent = envelopeRequestParser(feedbackResp.request()); + + expect(breadcrumbs).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + category: 'sentry.feedback', + data: { feedbackId: expect.any(String) }, + timestamp: expect.any(Number), + type: 'default', + }), + ]), + ); + + expect(feedbackEvent).toEqual({ + type: 'feedback', + breadcrumbs: expect.any(Array), + contexts: { + feedback: { + contact_email: 'janedoe@example.org', + message: 'my example feedback', + name: 'Jane Doe', + replay_id: replayEvent.event_id, + source: 'widget', + url: `${TEST_HOST}/index.html`, + }, + trace: { + trace_id: expect.stringMatching(/\w{32}/), + span_id: expect.stringMatching(/\w{16}/), + }, + }, + level: 'info', + tags: {}, + timestamp: expect.any(Number), + event_id: expect.stringMatching(/\w{32}/), + environment: 'production', + sdk: { + integrations: expect.arrayContaining(['Feedback']), + version: expect.any(String), + name: 'sentry.javascript.browser', + packages: expect.anything(), + settings: { + infer_ip: 'never', + }, + }, + request: { + url: `${TEST_HOST}/index.html`, + headers: { + 'User-Agent': expect.stringContaining(''), + }, + }, + platform: 'javascript', + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/template.html b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/template.html new file mode 100644 index 000000000000..57334d4ad2f1 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackAndReplay/template.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/init.js b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/init.js new file mode 100644 index 000000000000..27e5495f66a8 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/init.js @@ -0,0 +1,12 @@ +import * as Sentry from '@sentry/browser'; +// Import this separately so that generatePlugin can handle it for CDN scenarios +import { feedbackIntegration } from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + feedbackIntegration({ tags: { from: 'integration init' }, styleNonce: 'foo1234', scriptNonce: 'foo1234' }), + ], +}); diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/subject.js b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/subject.js new file mode 100644 index 000000000000..66adfd0f87d4 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/subject.js @@ -0,0 +1,4 @@ +window.__CSPVIOLATION__ = false; +document.addEventListener('securitypolicyviolation', () => { + window.__CSPVIOLATION__ = true; +}); diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/template.html b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/template.html new file mode 100644 index 000000000000..8039192f5787 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/template.html @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/test.ts b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/test.ts new file mode 100644 index 000000000000..cb683ce4fa36 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/test.ts @@ -0,0 +1,78 @@ +import { expect } from '@playwright/test'; +import { sentryTest, TEST_HOST } from '../../../utils/fixtures'; +import { envelopeRequestParser, getEnvelopeType, shouldSkipFeedbackTest } from '../../../utils/helpers'; + +sentryTest('should capture feedback', 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 { + return false; + } + }); + + const url = await getLocalTestUrl({ testDir: __dirname, handleLazyLoadedFeedback: true }); + + await page.goto(url); + await page.getByText('Report a Bug').click(); + 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', + tags: { + from: 'integration init', + }, + timestamp: expect.any(Number), + event_id: expect.stringMatching(/\w{32}/), + environment: 'production', + sdk: { + integrations: expect.arrayContaining(['Feedback']), + version: expect.any(String), + name: 'sentry.javascript.browser', + packages: expect.anything(), + settings: { + infer_ip: 'never', + }, + }, + request: { + url: `${TEST_HOST}/index.html`, + headers: { + 'User-Agent': expect.stringContaining(''), + }, + }, + platform: 'javascript', + }); + const cspViolation = await page.evaluate('window.__CSPVIOLATION__'); + expect(cspViolation).toBe(false); +}); 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..9cc67e0bf16b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/logger/test.ts @@ -0,0 +1,27 @@ +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()); + }); + + 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/Breadcrumbs/console/capture/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/console/capture/subject.js new file mode 100644 index 000000000000..d9ee50bf556f --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/console/capture/subject.js @@ -0,0 +1,10 @@ +console.log('One'); +console.warn('Two', { a: 1 }); +console.error('Error 2', { b: { c: [] } }); + +// Passed assertions _should not_ be captured +console.assert(1 + 1 === 2, 'math works'); +// Failed assertions _should_ be captured +console.assert(1 + 1 === 3, 'math broke'); + +Sentry.captureException('test exception'); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/console/capture/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/console/capture/test.ts new file mode 100644 index 000000000000..affd8537596d --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/console/capture/test.ts @@ -0,0 +1,44 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/browser'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('should capture console breadcrumbs', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.breadcrumbs).toEqual([ + { + category: 'console', + data: { arguments: ['One'], logger: 'console' }, + level: 'log', + message: 'One', + timestamp: expect.any(Number), + }, + { + category: 'console', + data: { arguments: ['Two', { a: 1 }], logger: 'console' }, + level: 'warning', + message: 'Two [object Object]', + timestamp: expect.any(Number), + }, + { + category: 'console', + data: { arguments: ['Error 2', { b: '[Object]' }], logger: 'console' }, + level: 'error', + message: 'Error 2 [object Object]', + timestamp: expect.any(Number), + }, + { + category: 'console', + data: { + arguments: ['math broke'], + logger: 'console', + }, + level: 'log', + message: 'Assertion failed: math broke', + timestamp: expect.any(Number), + }, + ]); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/console/init.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/console/init.js new file mode 100644 index 000000000000..36806d01c6d0 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/console/init.js @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + defaultIntegrations: false, + integrations: [Sentry.breadcrumbsIntegration()], + sampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/click/template.html b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/click/template.html new file mode 100644 index 000000000000..97d2b9069eb4 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/click/template.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/click/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/click/test.ts new file mode 100644 index 000000000000..549a47c0896c --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/click/test.ts @@ -0,0 +1,99 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb for clicks & debounces them for a second', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const promise = getFirstSentryEnvelopeRequest(page); + + await page.goto(url); + + await page.locator('#button1').click(); + + // not debounced because other target + await page.locator('#button2').click(); + // This should be debounced + await page.locator('#button2').click(); + + // Wait a second for the debounce to finish + await page.waitForTimeout(1000); + await page.locator('#button2').click(); + + const [eventData] = await Promise.all([promise, page.evaluate('Sentry.captureException("test exception")')]); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData.breadcrumbs).toEqual([ + { + timestamp: expect.any(Number), + category: 'ui.click', + message: 'body > button#button1[type="button"]', + }, + { + timestamp: expect.any(Number), + category: 'ui.click', + message: 'body > button#button2[type="button"]', + }, + { + timestamp: expect.any(Number), + category: 'ui.click', + message: 'body > button#button2[type="button"]', + }, + ]); +}); + +sentryTest( + 'uses the annotated component name in the breadcrumb messages and adds it to the data object', + async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const promise = getFirstSentryEnvelopeRequest(page); + + await page.goto(url); + await page.locator('#annotated-button').click(); + await page.locator('#annotated-button-2').click(); + + const [eventData] = await Promise.all([promise, page.evaluate('Sentry.captureException("test exception")')]); + + expect(eventData.breadcrumbs).toEqual([ + { + timestamp: expect.any(Number), + category: 'ui.click', + message: 'body > AnnotatedButton', + data: { 'ui.component_name': 'AnnotatedButton' }, + }, + { + timestamp: expect.any(Number), + category: 'ui.click', + message: 'body > StyledButton', + data: { 'ui.component_name': 'StyledButton' }, + }, + ]); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/clickWithError/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/clickWithError/subject.js new file mode 100644 index 000000000000..9a0c89788ea7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/clickWithError/subject.js @@ -0,0 +1,7 @@ +const click = new MouseEvent('click'); +function kaboom() { + throw new Error('lol'); +} +Object.defineProperty(click, 'target', { get: kaboom }); +const input = document.getElementById('input1'); +input.dispatchEvent(click); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/clickWithError/template.html b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/clickWithError/template.html new file mode 100644 index 000000000000..cba1da8d531d --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/clickWithError/template.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/clickWithError/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/clickWithError/test.ts new file mode 100644 index 000000000000..541e2b39bab9 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/clickWithError/test.ts @@ -0,0 +1,31 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/browser'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +// see: https://github.com/getsentry/sentry-javascript/issues/768 +sentryTest( + 'should record breadcrumb if accessing the target property of an event throws an exception', + async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + + const promise = getFirstSentryEnvelopeRequest(page); + + await page.locator('#input1').pressSequentially('test', { delay: 1 }); + + await page.evaluate('Sentry.captureException("test exception")'); + + const eventData = await promise; + + expect(eventData.breadcrumbs).toHaveLength(1); + expect(eventData.breadcrumbs).toEqual([ + { + category: 'ui.input', + message: 'body > input#input1[type="text"]', + timestamp: expect.any(Number), + }, + ]); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/customEvent/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/customEvent/subject.js new file mode 100644 index 000000000000..ca08cace4134 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/customEvent/subject.js @@ -0,0 +1,9 @@ +const input = document.getElementsByTagName('input')[0]; +input.addEventListener('build', function (evt) { + evt.stopPropagation(); +}); + +const customEvent = new CustomEvent('build', { detail: 1 }); +input.dispatchEvent(customEvent); + +Sentry.captureException('test exception'); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/customEvent/template.html b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/customEvent/template.html new file mode 100644 index 000000000000..a16ca41e45da --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/customEvent/template.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/customEvent/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/customEvent/test.ts new file mode 100644 index 000000000000..ac00200f9dde --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/customEvent/test.ts @@ -0,0 +1,18 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('breadcrumbs listener should not fail with custom event', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + let error = undefined; + page.on('pageerror', err => { + error = err; + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.breadcrumbs).toBeUndefined(); + expect(error).toBeUndefined(); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/init.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/init.js new file mode 100644 index 000000000000..36806d01c6d0 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/init.js @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + defaultIntegrations: false, + integrations: [Sentry.breadcrumbsIntegration()], + sampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/multipleTypes/template.html b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/multipleTypes/template.html new file mode 100644 index 000000000000..cba1da8d531d --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/multipleTypes/template.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/multipleTypes/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/multipleTypes/test.ts new file mode 100644 index 000000000000..4ecbb1ff539b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/multipleTypes/test.ts @@ -0,0 +1,49 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/browser'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest( + 'should correctly capture multiple consecutive breadcrumbs if they are of different type', + async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + + const promise = getFirstSentryEnvelopeRequest(page); + + // These inputs will be debounced + await page.locator('#input1').pressSequentially('abc', { delay: 1 }); + await page.locator('#input1').pressSequentially('def', { delay: 1 }); + await page.locator('#input1').pressSequentially('ghi', { delay: 1 }); + + await page.locator('#input1').click(); + await page.locator('#input1').click(); + await page.locator('#input1').click(); + + // This input should not be debounced + await page.locator('#input1').pressSequentially('jkl', { delay: 1 }); + + await page.evaluate('Sentry.captureException("test exception")'); + + const eventData = await promise; + + expect(eventData.breadcrumbs).toEqual([ + { + category: 'ui.input', + message: 'body > input#input1[type="text"]', + timestamp: expect.any(Number), + }, + { + category: 'ui.click', + message: 'body > input#input1[type="text"]', + timestamp: expect.any(Number), + }, + { + category: 'ui.input', + message: 'body > input#input1[type="text"]', + timestamp: expect.any(Number), + }, + ]); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/textInput/template.html b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/textInput/template.html new file mode 100644 index 000000000000..38934ca803a4 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/textInput/template.html @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/textInput/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/textInput/test.ts new file mode 100644 index 000000000000..d0859202a54e --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/dom/textInput/test.ts @@ -0,0 +1,106 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb for events on inputs & debounced them', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const promise = getFirstSentryEnvelopeRequest(page); + + await page.goto(url); + + // Not debounced because other event type + await page.locator('#input1').pressSequentially('John', { delay: 1 }); + + // This should be debounced + await page.locator('#input1').pressSequentially('Abby', { delay: 1 }); + + // not debounced because other target + await page.locator('#input2').pressSequentially('Anne', { delay: 1 }); + + // Wait a second for the debounce to finish + await page.waitForTimeout(1000); + await page.locator('#input2').pressSequentially('John', { delay: 1 }); + + await page.evaluate('Sentry.captureException("test exception")'); + + const eventData = await promise; + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData.breadcrumbs).toEqual([ + { + timestamp: expect.any(Number), + category: 'ui.input', + message: 'body > input#input1[type="text"]', + }, + { + timestamp: expect.any(Number), + category: 'ui.input', + message: 'body > input#input2[type="text"]', + }, + { + timestamp: expect.any(Number), + category: 'ui.input', + message: 'body > input#input2[type="text"]', + }, + ]); +}); + +sentryTest( + 'includes the annotated component name within the breadcrumb message and data', + async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const promise = getFirstSentryEnvelopeRequest(page); + + await page.goto(url); + + await page.locator('#annotated-input').pressSequentially('John', { delay: 1 }); + await page.locator('#annotated-input-2').pressSequentially('John', { delay: 1 }); + + await page.evaluate('Sentry.captureException("test exception")'); + const eventData = await promise; + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData.breadcrumbs).toEqual([ + { + timestamp: expect.any(Number), + category: 'ui.input', + message: 'body > AnnotatedInput', + data: { 'ui.component_name': 'AnnotatedInput' }, + }, + { + timestamp: expect.any(Number), + category: 'ui.input', + message: 'body > StyledInput', + data: { 'ui.component_name': 'StyledInput' }, + }, + ]); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/subject.js new file mode 100644 index 000000000000..1cbadc6e36e6 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/subject.js @@ -0,0 +1,3 @@ +fetch('http://sentry-test.io/foo').then(() => { + Sentry.captureException('test error'); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/test.ts new file mode 100644 index 000000000000..6a5024caee03 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/get/test.ts @@ -0,0 +1,36 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb for basic GET request', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'fetch', + type: 'http', + data: { + method: 'GET', + status_code: 200, + url: 'http://sentry-test.io/foo', + }, + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/subject.js new file mode 100644 index 000000000000..2c014f821a43 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/subject.js @@ -0,0 +1,3 @@ +fetch(new Request('http://sentry-test.io/foo')).then(() => { + Sentry.captureException('test error'); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/test.ts new file mode 100644 index 000000000000..fc125817725e --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/getWithRequestObj/test.ts @@ -0,0 +1,36 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb for basic GET request that uses request object', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'fetch', + type: 'http', + data: { + method: 'GET', + status_code: 200, + url: 'http://sentry-test.io/foo', + }, + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/init.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/init.js new file mode 100644 index 000000000000..36806d01c6d0 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/init.js @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + defaultIntegrations: false, + integrations: [Sentry.breadcrumbsIntegration()], + sampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/subject.js new file mode 100644 index 000000000000..e1545e8060c6 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/subject.js @@ -0,0 +1,11 @@ +fetch('http://sentry-test.io/foo', { + method: 'POST', + body: '{"my":"body"}', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Cache: 'no-cache', + }, +}).then(() => { + Sentry.captureException('test error'); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/test.ts new file mode 100644 index 000000000000..6658c2628f60 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/test.ts @@ -0,0 +1,36 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb for POST request', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'fetch', + type: 'http', + data: { + method: 'POST', + status_code: 200, + url: 'http://sentry-test.io/foo', + }, + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/statusCode/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/statusCode/subject.js new file mode 100644 index 000000000000..1cbadc6e36e6 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/statusCode/subject.js @@ -0,0 +1,3 @@ +fetch('http://sentry-test.io/foo').then(() => { + Sentry.captureException('test error'); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/statusCode/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/statusCode/test.ts new file mode 100644 index 000000000000..03b9b636d9e9 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/statusCode/test.ts @@ -0,0 +1,70 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb with log level for 4xx response code', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', async route => { + await route.fulfill({ + status: 404, + contentType: 'text/plain', + body: 'Not Found!', + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'fetch', + type: 'http', + data: { + method: 'GET', + status_code: 404, + url: 'http://sentry-test.io/foo', + }, + level: 'warning', + }); + + await page.route('**/foo', async route => { + await route.fulfill({ + status: 500, + contentType: 'text/plain', + body: 'Internal Server Error', + }); + }); +}); + +sentryTest('captures Breadcrumb with log level for 5xx response code', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', async route => { + await route.fulfill({ + status: 500, + contentType: 'text/plain', + body: 'Internal Server Error', + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'fetch', + type: 'http', + data: { + method: 'GET', + status_code: 500, + url: 'http://sentry-test.io/foo', + }, + level: 'error', + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/init.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/init.js new file mode 100644 index 000000000000..36806d01c6d0 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/init.js @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + defaultIntegrations: false, + integrations: [Sentry.breadcrumbsIntegration()], + sampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/navigation/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/navigation/subject.js new file mode 100644 index 000000000000..dd1d47ef4dff --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/navigation/subject.js @@ -0,0 +1,7 @@ +history.pushState({}, '', '/foo'); +history.pushState({}, '', '/bar?a=1#fragment'); +history.pushState({}, '', {}); +history.pushState({}, '', null); +history.replaceState({}, '', '/bar?a=1#fragment'); + +Sentry.captureException('test exception'); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/navigation/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/navigation/test.ts new file mode 100644 index 000000000000..1eb7f55b60cd --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/history/navigation/test.ts @@ -0,0 +1,45 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/browser'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('should record history changes as navigation breadcrumbs', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.breadcrumbs).toEqual([ + { + category: 'navigation', + data: { + from: '/index.html', + to: '/foo', + }, + timestamp: expect.any(Number), + }, + { + category: 'navigation', + data: { + from: '/foo', + to: '/bar?a=1#fragment', + }, + timestamp: expect.any(Number), + }, + { + category: 'navigation', + data: { + from: '/bar?a=1#fragment', + to: '/[object%20Object]', + }, + timestamp: expect.any(Number), + }, + { + category: 'navigation', + data: { + from: '/[object%20Object]', + to: '/bar?a=1#fragment', + }, + timestamp: expect.any(Number), + }, + ]); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/subject.js new file mode 100644 index 000000000000..8202bb03803b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/subject.js @@ -0,0 +1,10 @@ +const xhr = new XMLHttpRequest(); + +xhr.open('GET', 'http://sentry-test.io/foo'); +xhr.send(); + +xhr.addEventListener('readystatechange', function () { + if (xhr.readyState === 4) { + Sentry.captureException('test error'); + } +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/test.ts new file mode 100644 index 000000000000..02275b0adf2a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/get/test.ts @@ -0,0 +1,37 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb for basic GET request', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + 'Content-Length': '', + }, + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'xhr', + type: 'http', + data: { + method: 'GET', + status_code: 200, + url: 'http://sentry-test.io/foo', + }, + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/init.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/init.js new file mode 100644 index 000000000000..36806d01c6d0 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/init.js @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + defaultIntegrations: false, + integrations: [Sentry.breadcrumbsIntegration()], + sampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/subject.js new file mode 100644 index 000000000000..888d961ae9c5 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/subject.js @@ -0,0 +1,12 @@ +const xhr = new XMLHttpRequest(); + +xhr.open('POST', 'http://sentry-test.io/foo'); +xhr.setRequestHeader('Accept', 'application/json'); +xhr.setRequestHeader('Content-Type', 'application/json'); +xhr.send('{"my":"body"}'); + +xhr.addEventListener('readystatechange', function () { + if (xhr.readyState === 4) { + Sentry.captureException('test error'); + } +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/test.ts new file mode 100644 index 000000000000..7de33d189f37 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/post/test.ts @@ -0,0 +1,36 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb for POST request', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', route => { + return route.fulfill({ + status: 200, + body: JSON.stringify({ + userNames: ['John', 'Jane'], + }), + headers: { + 'Content-Type': 'application/json', + }, + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'xhr', + type: 'http', + data: { + method: 'POST', + status_code: 200, + url: 'http://sentry-test.io/foo', + }, + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/statusCode/subject.js b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/statusCode/subject.js new file mode 100644 index 000000000000..8202bb03803b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/statusCode/subject.js @@ -0,0 +1,10 @@ +const xhr = new XMLHttpRequest(); + +xhr.open('GET', 'http://sentry-test.io/foo'); +xhr.send(); + +xhr.addEventListener('readystatechange', function () { + if (xhr.readyState === 4) { + Sentry.captureException('test error'); + } +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/statusCode/test.ts b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/statusCode/test.ts new file mode 100644 index 000000000000..d17613cc1da1 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/xhr/statusCode/test.ts @@ -0,0 +1,70 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('captures Breadcrumb with log level for 4xx response code', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', async route => { + await route.fulfill({ + status: 404, + contentType: 'text/plain', + body: 'Not Found!', + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'xhr', + type: 'http', + data: { + method: 'GET', + status_code: 404, + url: 'http://sentry-test.io/foo', + }, + level: 'warning', + }); + + await page.route('**/foo', async route => { + await route.fulfill({ + status: 500, + contentType: 'text/plain', + body: 'Internal Server Error', + }); + }); +}); + +sentryTest('captures Breadcrumb with log level for 5xx response code', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/foo', async route => { + await route.fulfill({ + status: 500, + contentType: 'text/plain', + body: 'Internal Server Error', + }); + }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + + expect(eventData?.breadcrumbs?.length).toBe(1); + expect(eventData.breadcrumbs![0]).toEqual({ + timestamp: expect.any(Number), + category: 'xhr', + type: 'http', + data: { + method: 'GET', + status_code: 500, + url: 'http://sentry-test.io/foo', + }, + level: 'error', + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/ContextLines/init.js b/dev-packages/browser-integration-tests/suites/integrations/ContextLines/init.js new file mode 100644 index 000000000000..cdcb68f2b48f --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/ContextLines/init.js @@ -0,0 +1,9 @@ +import * as Sentry from '@sentry/browser'; +import { contextLinesIntegration } from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [contextLinesIntegration()], +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/ContextLines/inline/template.html b/dev-packages/browser-integration-tests/suites/integrations/ContextLines/inline/template.html new file mode 100644 index 000000000000..acb69e682644 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/ContextLines/inline/template.html @@ -0,0 +1,12 @@ + + + + + + + + +
+ Some text... + + diff --git a/dev-packages/browser-integration-tests/suites/integrations/ContextLines/inline/test.ts b/dev-packages/browser-integration-tests/suites/integrations/ContextLines/inline/test.ts new file mode 100644 index 000000000000..57d0d5fc6d08 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/ContextLines/inline/test.ts @@ -0,0 +1,53 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../../utils/helpers'; + +sentryTest( + 'should add source context lines around stack frames from errors in Html inline JS', + async ({ getLocalTestUrl, page, browserName }) => { + if (browserName === 'webkit') { + // The error we're throwing in this test is thrown as "Script error." in Webkit. + // We filter "Script error." out by default in `InboundFilters`. + // I don't think there's much value to disable InboundFilters defaults for this test, + // given that most of our users won't do that either. + // Let's skip it instead for Webkit. + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const eventReqPromise = waitForErrorRequestOnUrl(page, url); + await page.waitForFunction('window.Sentry'); + + const clickPromise = page.locator('#inline-error-btn').click(); + + const [req] = await Promise.all([eventReqPromise, clickPromise]); + + const eventData = envelopeRequestParser(req); + + expect(eventData.exception?.values).toHaveLength(1); + + const exception = eventData.exception?.values?.[0]; + + expect(exception).toMatchObject({ + stacktrace: { + frames: [ + { + pre_context: ['', '', '', ' ', ' ', ' '], + context_line: + ' ', + post_context: [ + expect.stringContaining('', + ' Some text...', + ' ', + '', + '
', + '', + ], + }, + ], + }, + }); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/integrations/ContextLines/noAddedLines/subject.js b/dev-packages/browser-integration-tests/suites/integrations/ContextLines/noAddedLines/subject.js new file mode 100644 index 000000000000..744649fb291c --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/ContextLines/noAddedLines/subject.js @@ -0,0 +1,3 @@ +document.getElementById('script-error-btn').addEventListener('click', () => { + throw new Error('Error without context lines'); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/ContextLines/noAddedLines/template.html b/dev-packages/browser-integration-tests/suites/integrations/ContextLines/noAddedLines/template.html new file mode 100644 index 000000000000..80569790143a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/ContextLines/noAddedLines/template.html @@ -0,0 +1,12 @@ + + + + + + + + +