From 64f086f8c39b71c2c249116022f9e316eff72ba6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 03:12:52 +0000 Subject: [PATCH 01/52] build(deps): bump actions/cache from 4.3.0 to 5.0.1 Bumps [actions/cache](https://github.com/actions/cache) from 4.3.0 to 5.0.1. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/0057852bfaa89a56745cba8c7296529d2fc39830...9255dc7a253b0ccc959486e2bca901246202afeb) --- updated-dependencies: - dependency-name: actions/cache dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/test-electron.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-electron.yml b/.github/workflows/test-electron.yml index 2f80d22020..9221072085 100644 --- a/.github/workflows/test-electron.yml +++ b/.github/workflows/test-electron.yml @@ -30,7 +30,7 @@ jobs: if: ${{ !env.ACT }} run: | echo "::set-output name=dir::$(npm config get cache)" - - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + - uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 id: npm-cache if: ${{ !env.ACT }} with: From d4183533afdc6401a3ee506616a010d06fc50679 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 03:13:02 +0000 Subject: [PATCH 02/52] build(deps): bump actions/checkout from 6.0.0 to 6.0.1 Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.0 to 6.0.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/1af3b93b6815bc44a9784bd300feb67ff0d1eeb3...8e8c483db84b4bee98b60c0593521ed34d9990e8) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/aws-lambda.yml | 2 +- .github/workflows/codeql.yml | 2 +- .github/workflows/pr-diff.yml | 4 ++-- .github/workflows/scorecard.yml | 4 ++-- .github/workflows/test-electron.yml | 2 +- .github/workflows/update-dependencies.yml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/aws-lambda.yml b/.github/workflows/aws-lambda.yml index e6bc3bcde7..1cf126c234 100644 --- a/.github/workflows/aws-lambda.yml +++ b/.github/workflows/aws-lambda.yml @@ -21,7 +21,7 @@ jobs: - run: sam --version - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Install Ruby uses: ruby/setup-ruby@d697be2f83c6234b20877c3b5eac7a7f342f0d0c # v1.269.0 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index cda566a73d..b0377137c5 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -43,7 +43,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/pr-diff.yml b/.github/workflows/pr-diff.yml index f400672148..bc9953bc01 100644 --- a/.github/workflows/pr-diff.yml +++ b/.github/workflows/pr-diff.yml @@ -14,7 +14,7 @@ jobs: node-version: 18.x - name: Checkout base branch - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.base_ref }} @@ -29,7 +29,7 @@ jobs: cat packages/browser/dist/bugsnag.min.js | gzip | wc -c > .diff/size-before-gzipped - name: Checkout PR branch - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: clean: false diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index e4264251c0..fef6b0fc6d 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -32,7 +32,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -76,5 +76,5 @@ jobs: name: "Checksum validation of Gradle Wrappers" runs-on: ubuntu-latest steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: gradle/actions/wrapper-validation@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0 diff --git a/.github/workflows/test-electron.yml b/.github/workflows/test-electron.yml index 2f80d22020..caad81fbe6 100644 --- a/.github/workflows/test-electron.yml +++ b/.github/workflows/test-electron.yml @@ -14,7 +14,7 @@ jobs: os: [ ubuntu-latest ] steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index bc833af7f8..a6f718cbe8 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -28,7 +28,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} REVIEWER: gingerbenw steps: - - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: next From be1e01f296cfd2e0c67775ca92681a5bb70c9d3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 03:13:22 +0000 Subject: [PATCH 03/52] build(deps): bump github/codeql-action from 4.31.7 to 4.31.8 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.7 to 4.31.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/cf1bb45a277cb3c205638b2cd5c984db1c46a412...1b168cd39490f61582a9beae412bb7057a6b2c4e) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 6 +++--- .github/workflows/scorecard.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index cda566a73d..56b95ab0a6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -47,7 +47,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7 + uses: github/codeql-action/init@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -61,7 +61,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, 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@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7 + uses: github/codeql-action/autobuild@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -74,6 +74,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7 + uses: github/codeql-action/analyze@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index e4264251c0..d9e943bb9e 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -68,7 +68,7 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7 + uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 with: sarif_file: results.sarif From 81252219ed17cbc6a5e04b1135f9d2f66ab7a051 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 03:13:29 +0000 Subject: [PATCH 04/52] build(deps): bump ruby/setup-ruby from 1.269.0 to 1.270.0 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.269.0 to 1.270.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Changelog](https://github.com/ruby/setup-ruby/blob/master/release.rb) - [Commits](https://github.com/ruby/setup-ruby/compare/d697be2f83c6234b20877c3b5eac7a7f342f0d0c...ac793fdd38cc468a4dd57246fa9d0e868aba9085) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-version: 1.270.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/aws-lambda.yml | 2 +- .github/workflows/update-dependencies.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/aws-lambda.yml b/.github/workflows/aws-lambda.yml index e6bc3bcde7..a02c8e93f1 100644 --- a/.github/workflows/aws-lambda.yml +++ b/.github/workflows/aws-lambda.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - name: Install Ruby - uses: ruby/setup-ruby@d697be2f83c6234b20877c3b5eac7a7f342f0d0c # v1.269.0 + uses: ruby/setup-ruby@ac793fdd38cc468a4dd57246fa9d0e868aba9085 # v1.270.0 with: ruby-version: '3.1' diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index bc833af7f8..e203c6f23e 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -40,7 +40,7 @@ jobs: - run: git submodule update --init --recursive - name: Install ruby - uses: ruby/setup-ruby@d697be2f83c6234b20877c3b5eac7a7f342f0d0c # v1.269.0 + uses: ruby/setup-ruby@ac793fdd38cc468a4dd57246fa9d0e868aba9085 # v1.270.0 with: ruby-version: 2.7 From 1fea0f6d8edffb26179b4250583b5ed9d2691f35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 03:13:33 +0000 Subject: [PATCH 05/52] build(deps): bump actions/upload-artifact from 5.0.0 to 6.0.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5.0.0 to 6.0.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/330a01c490aca151604b8cf639adc76d48f6c5d4...b7c566a772e6b6bfb58ed0dc250532a479d7789f) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/scorecard.yml | 2 +- .github/workflows/test-electron.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index e4264251c0..edd24db77b 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -59,7 +59,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: SARIF file path: results.sarif diff --git a/.github/workflows/test-electron.yml b/.github/workflows/test-electron.yml index 2f80d22020..9dce9b4368 100644 --- a/.github/workflows/test-electron.yml +++ b/.github/workflows/test-electron.yml @@ -66,7 +66,7 @@ jobs: START_LOCAL_NPM: 1 VERBOSE: 1 ELECTRON_VERSION: ${{ matrix.electron }} - - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 if: failure() with: name: cucumber-failures From 82db74352561e1623a9d482992d39ecd961209b5 Mon Sep 17 00:00:00 2001 From: Bumpsnag bot <> Date: Mon, 19 Jan 2026 15:31:26 +0000 Subject: [PATCH 06/52] Update bugsnag-android to v6.22.0 [full ci] --- CHANGELOG.md | 6 ++++++ packages/react-native/android/build.gradle | 4 ++-- packages/react-native/prepare-android-vendor.config | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15e1c57cf0..dd44338523 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased] + +### Changed + +Update bugsnag-android to [v6.22.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.22.0) [#2656](https://github.com/bugsnag/bugsnag-js/pull/2656) + ## [8.8.0] - 2026-01-20 This release adds support for notitfying failed network requests using the new `plugin-network-instrumentation` package diff --git a/packages/react-native/android/build.gradle b/packages/react-native/android/build.gradle index beb3d75f1a..b3cbfbe780 100644 --- a/packages/react-native/android/build.gradle +++ b/packages/react-native/android/build.gradle @@ -45,8 +45,8 @@ android { } dependencies { - api "com.bugsnag:bugsnag-android:6.20.0" - api "com.bugsnag:bugsnag-plugin-react-native:6.20.0" + api "com.bugsnag:bugsnag-android:6.22.0" + api "com.bugsnag:bugsnag-plugin-react-native:6.22.0" implementation 'com.facebook.react:react-native:+' testImplementation "junit:junit:4.12" diff --git a/packages/react-native/prepare-android-vendor.config b/packages/react-native/prepare-android-vendor.config index d8e74d5e8f..fbf5b0c027 100644 --- a/packages/react-native/prepare-android-vendor.config +++ b/packages/react-native/prepare-android-vendor.config @@ -1,2 +1,2 @@ version -6.20.0 +6.22.0 From 2b9330df54ac333e4977118df83627a5e22000b4 Mon Sep 17 00:00:00 2001 From: Bumpsnag bot <> Date: Mon, 26 Jan 2026 13:48:11 +0000 Subject: [PATCH 07/52] Update bugsnag-cocoa to v6.35.0 [full ci] --- CHANGELOG.md | 7 ++++++- packages/react-native/ios/vendor/bugsnag-cocoa | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15e1c57cf0..a04fe7e4ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,14 @@ # Changelog +## [Unreleased] + +### Dependencies + +Update bugsnag-cocoa to [v6.35.0](https//github.com/bugsnag/bugsnag-cocoa/releases/tag/v6.35.0) [#2663](https://github.com/bugsnag/bugsnag-js/pull/2663) + ## [8.8.0] - 2026-01-20 This release adds support for notitfying failed network requests using the new `plugin-network-instrumentation` package - ### Added (plugin-network-instrumentation) Add new plugin to notify failed network requests [#2647](https://github.com/bugsnag/bugsnag-js/pull/2647) diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa b/packages/react-native/ios/vendor/bugsnag-cocoa index f4348c29cc..ae1094f4df 160000 --- a/packages/react-native/ios/vendor/bugsnag-cocoa +++ b/packages/react-native/ios/vendor/bugsnag-cocoa @@ -1 +1 @@ -Subproject commit f4348c29cc881ce80e4e22a46f29d7f5a18e924b +Subproject commit ae1094f4dfd3f946f00f27e5e6771fd36243a342 From 15894af087b39135911b40fd3017434ee26ebcc8 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Tue, 27 Jan 2026 09:49:48 +0000 Subject: [PATCH 08/52] Run e2e tests with Ruby 4 (#2666) * Set Content Type header * Use latest MR image --------- Co-authored-by: Steve Kirkland --- dockerfiles/Dockerfile.browser | 2 +- dockerfiles/Dockerfile.node | 2 +- test/node/features/steps/server_fixture_request_steps.rb | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dockerfiles/Dockerfile.browser b/dockerfiles/Dockerfile.browser index 2eb7d7da54..f249209a51 100644 --- a/dockerfiles/Dockerfile.browser +++ b/dockerfiles/Dockerfile.browser @@ -55,7 +55,7 @@ RUN find . -name package.json -type f -mindepth 2 -maxdepth 3 ! -path "./node_mo RUN rm -fr **/*/node_modules/ # The maze-runner browser tests (W3C protocol) -FROM 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:v10.10.1-cli AS browser-maze-runner +FROM 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v10-cli AS browser-maze-runner COPY --from=browser-feature-builder /app/test/browser /app/test/browser/ WORKDIR /app/test/browser diff --git a/dockerfiles/Dockerfile.node b/dockerfiles/Dockerfile.node index 8d88d98c83..136416474f 100644 --- a/dockerfiles/Dockerfile.node +++ b/dockerfiles/Dockerfile.node @@ -22,7 +22,7 @@ RUN npm pack --verbose packages/plugin-restify/ RUN npm pack --verbose packages/plugin-hono/ # The maze-runner node tests -FROM 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:v10.10.1-cli AS node-maze-runner +FROM 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v10-cli AS node-maze-runner WORKDIR /app/ COPY packages/node/ . COPY test/node/features test/node/features diff --git a/test/node/features/steps/server_fixture_request_steps.rb b/test/node/features/steps/server_fixture_request_steps.rb index 28adb78fe2..657576014e 100644 --- a/test/node/features/steps/server_fixture_request_steps.rb +++ b/test/node/features/steps/server_fixture_request_steps.rb @@ -5,7 +5,9 @@ # @step_input reqbody [String] urlencoded data to send. # @step_input url [String] The URL to post data to. When("I POST the data {string} to the URL {string}") do |reqbody, url| - Net::HTTP.post(URI(url), reqbody) + Net::HTTP.post(URI(url), + reqbody, + 'Content-Type' => 'application/x-www-form-urlencoded') end When('I open the URL {string} tolerating any error') do |url| From 38f92acf066f38cd258016072af5635e002cfa35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 03:46:13 +0000 Subject: [PATCH 09/52] build(deps): bump actions/cache from 5.0.1 to 5.0.3 Bumps [actions/cache](https://github.com/actions/cache) from 5.0.1 to 5.0.3. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/9255dc7a253b0ccc959486e2bca901246202afeb...cdf6c1fa76f9f475f3d7449005a359c84ca0f306) --- updated-dependencies: - dependency-name: actions/cache dependency-version: 5.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/test-electron.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-electron.yml b/.github/workflows/test-electron.yml index 36338b2c2e..2197ae6352 100644 --- a/.github/workflows/test-electron.yml +++ b/.github/workflows/test-electron.yml @@ -30,7 +30,7 @@ jobs: if: ${{ !env.ACT }} run: | echo "::set-output name=dir::$(npm config get cache)" - - uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 + - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 id: npm-cache if: ${{ !env.ACT }} with: From 6e367adbe974c9a47249b18c7cd15973cbc1a7be Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Mon, 2 Feb 2026 10:21:12 +0000 Subject: [PATCH 10/52] Merge pull request #2662 from bugsnag/test/react-native-http-errors Mark HTTP errors as handled instead of unhandled --- CHANGELOG.md | 4 +++ .../network-instrumentation.js | 2 +- .../test/network-instrumentation-xhr.test.ts | 2 +- .../test/network-instrumentation.test.ts | 2 +- scripts/generate-react-native-fixture.js | 3 ++- .../react-native/features/breadcrumbs.feature | 7 +++++ .../NetworkJsScenario.json | 11 ++++++++ .../fixtures/expected_http_errors/401.json | 8 ++++++ .../fixtures/expected_http_errors/500.json | 8 ++++++ .../core/NetworkBreadcrumbsJsScenario.js | 15 +++++++++++ .../scenarios/core/NetworkRequestScenario.js | 27 +++++++++++++++++++ .../scenario-launcher/scenarios/core/index.js | 4 +++ .../react-native/features/http_errors.feature | 19 +++++++++++++ 13 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 test/react-native/features/fixtures/expected_breadcrumbs/NetworkJsScenario.json create mode 100644 test/react-native/features/fixtures/expected_http_errors/401.json create mode 100644 test/react-native/features/fixtures/expected_http_errors/500.json create mode 100644 test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkBreadcrumbsJsScenario.js create mode 100644 test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkRequestScenario.js create mode 100644 test/react-native/features/http_errors.feature diff --git a/CHANGELOG.md b/CHANGELOG.md index 816dd090ff..286dbd0c9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ Update bugsnag-android to [v6.22.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.22.0) [#2656](https://github.com/bugsnag/bugsnag-js/pull/2656) +### Fixed + +(plugin-network-instrumentation) Report HTTP Errors as handled [#2662](https://github.com/bugsnag/bugsnag-js/pull/2662) + ## [8.8.1] - 2026-01-26 ### Fixed diff --git a/packages/plugin-network-instrumentation/network-instrumentation.js b/packages/plugin-network-instrumentation/network-instrumentation.js index f0ac3b6812..530edb9fef 100644 --- a/packages/plugin-network-instrumentation/network-instrumentation.js +++ b/packages/plugin-network-instrumentation/network-instrumentation.js @@ -136,7 +136,7 @@ module.exports = (config = {}, global = window) => { const handledState = { severity: 'error', - unhandled: true, + unhandled: false, severityReason: { type: 'httpError' } } diff --git a/packages/plugin-network-instrumentation/test/network-instrumentation-xhr.test.ts b/packages/plugin-network-instrumentation/test/network-instrumentation-xhr.test.ts index b06796d394..63f28e4e90 100644 --- a/packages/plugin-network-instrumentation/test/network-instrumentation-xhr.test.ts +++ b/packages/plugin-network-instrumentation/test/network-instrumentation-xhr.test.ts @@ -135,7 +135,7 @@ describe('plugin-network-instrumentation', () => { expect(event.exceptions[0].errorMessage).toBe('404: https://api.example.com/users/123') expect(event.context).toBe('POST api.example.com') expect(event.severity).toBe('error') - expect(event.unhandled).toBe(true) + expect(event.unhandled).toBe(false) expect(event.severityReason.type).toBe('httpError') // Verify request metadata diff --git a/packages/plugin-network-instrumentation/test/network-instrumentation.test.ts b/packages/plugin-network-instrumentation/test/network-instrumentation.test.ts index 5d3407aed9..fda8520bfd 100644 --- a/packages/plugin-network-instrumentation/test/network-instrumentation.test.ts +++ b/packages/plugin-network-instrumentation/test/network-instrumentation.test.ts @@ -87,7 +87,7 @@ describe('plugin-network-instrumentation', () => { expect(event.exceptions[0].errorMessage).toBe('404: https://example.com/api/users') expect(event.context).toBe('GET example.com') expect(event.severity).toBe('error') - expect(event.unhandled).toBe(true) + expect(event.unhandled).toBe(false) expect(event.severityReason.type).toBe('httpError') expect(event.request.url).toBe('https://example.com/api/users') expect(event.request.httpMethod).toBe('GET') diff --git a/scripts/generate-react-native-fixture.js b/scripts/generate-react-native-fixture.js index c4b79085c6..a151a271df 100644 --- a/scripts/generate-react-native-fixture.js +++ b/scripts/generate-react-native-fixture.js @@ -40,7 +40,8 @@ const replacementFilesDir = resolve(ROOT_DIR, 'test/react-native/features/fixtur const INTERNAL_DEPENDENCIES = [ '@bugsnag/react-native', - '@bugsnag/request-tracker' + '@bugsnag/request-tracker', + '@bugsnag/plugin-network-instrumentation' ] // make sure we install a compatible versions of peer dependencies diff --git a/test/react-native/features/breadcrumbs.feature b/test/react-native/features/breadcrumbs.feature index 7274fd6a9c..13388a2cc5 100644 --- a/test/react-native/features/breadcrumbs.feature +++ b/test/react-native/features/breadcrumbs.feature @@ -47,3 +47,10 @@ Scenario: Manual breadcrumbs (Native) | ios | NSException | And the exception "message" equals "BreadcrumbsNativeManualScenario" And the event contains a breadcrumb matching the JSON fixture in "features/fixtures/expected_breadcrumbs/NativeManualScenario.json" + +Scenario: Network breadcrumbs (JS) + When I run "NetworkBreadcrumbsJsScenario" + Then I wait to receive an error + And the exception "errorClass" equals "Error" + And the exception "message" equals "NetworkBreadcrumbsJsScenario" + And the event contains a breadcrumb matching the JSON fixture in "features/fixtures/expected_breadcrumbs/NetworkJsScenario.json" \ No newline at end of file diff --git a/test/react-native/features/fixtures/expected_breadcrumbs/NetworkJsScenario.json b/test/react-native/features/fixtures/expected_breadcrumbs/NetworkJsScenario.json new file mode 100644 index 0000000000..79b9c3026e --- /dev/null +++ b/test/react-native/features/fixtures/expected_breadcrumbs/NetworkJsScenario.json @@ -0,0 +1,11 @@ +{ + "type": "request", + "name": "XMLHttpRequest succeeded", + "timestamp": "^\\d{4}\\-\\d{2}\\-\\d{2}T\\d{2}:\\d{2}:[\\d\\.]+Z?$", + "metaData": { + "status": "NUMBER", + "method": "GET", + "url": "^http:\/\/[0-9.:]*\/reflect$", + "duration": "NUMBER" + } +} \ No newline at end of file diff --git a/test/react-native/features/fixtures/expected_http_errors/401.json b/test/react-native/features/fixtures/expected_http_errors/401.json new file mode 100644 index 0000000000..e719a18b29 --- /dev/null +++ b/test/react-native/features/fixtures/expected_http_errors/401.json @@ -0,0 +1,8 @@ +[ + { + "message": "^401: .*\/reflect\/[?]status=401$", + "errorClass": "HTTPError", + "type": "reactnativejs", + "stacktrace": "IGNORE" + } +] diff --git a/test/react-native/features/fixtures/expected_http_errors/500.json b/test/react-native/features/fixtures/expected_http_errors/500.json new file mode 100644 index 0000000000..ccbe396790 --- /dev/null +++ b/test/react-native/features/fixtures/expected_http_errors/500.json @@ -0,0 +1,8 @@ +[ + { + "message": "^500: .*\/reflect\/[?]status=500$", + "errorClass": "HTTPError", + "type": "reactnativejs", + "stacktrace": "IGNORE" + } +] diff --git a/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkBreadcrumbsJsScenario.js b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkBreadcrumbsJsScenario.js new file mode 100644 index 0000000000..a781356bc7 --- /dev/null +++ b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkBreadcrumbsJsScenario.js @@ -0,0 +1,15 @@ +import Scenario from './Scenario' +import Bugsnag from '@bugsnag/react-native' + +export class NetworkBreadcrumbsJsScenario extends Scenario { + constructor (nativeConfig, jsConfig, scenarioData) { + super() + this.reflectEndpoint = nativeConfig.endpoints.notify.replace('/notify', '/reflect') + } + + run () { + fetch(this.reflectEndpoint).then(() => { + Bugsnag.notify(new Error('NetworkBreadcrumbsJsScenario')) + }) + } +} diff --git a/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkRequestScenario.js b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkRequestScenario.js new file mode 100644 index 0000000000..f69b85eb72 --- /dev/null +++ b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkRequestScenario.js @@ -0,0 +1,27 @@ +import Scenario from './Scenario' +import Bugsnag from '@bugsnag/react-native' +import BugsnagPluginNetworkInstrumentation from '@bugsnag/plugin-network-instrumentation' + +export class NetworkRequestScenario extends Scenario { + constructor (nativeConfig, jsConfig, scenarioData) { + super() + this.reflectEndpoint = nativeConfig.endpoints.notify.replace('/notify', '/reflect') + this.statusCode = scenarioData + + const plugin = new BugsnagPluginNetworkInstrumentation({ + maxRequestSize: 1024, + maxResponseSize: 1024 + }) + + jsConfig.plugins = jsConfig.plugins || [] + jsConfig.plugins.push(plugin) + } + + run () { + const url = new URL(this.reflectEndpoint) + url.searchParams.append('status', this.statusCode) + fetch(url).catch((err) => { + Bugsnag.notify(err) + }) + } +} diff --git a/test/react-native/features/fixtures/scenario-launcher/scenarios/core/index.js b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/index.js index 988e1529a8..1075c4c8c6 100644 --- a/test/react-native/features/fixtures/scenario-launcher/scenarios/core/index.js +++ b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/index.js @@ -35,6 +35,7 @@ export { BreadcrumbsAutomaticErrorScenario } from './BreadcrumbsAutomaticErrorSc export { BreadcrumbsJsManualScenario } from './BreadcrumbsJsManualScenario' export { BreadcrumbsNativeManualScenario } from './BreadcrumbsNativeManualScenario' export { BreadcrumbsNullEnabledBreadcrumbTypesScenario } from './BreadcrumbsNullEnabledBreadcrumbTypesScenario' +export { NetworkBreadcrumbsJsScenario } from './NetworkBreadcrumbsJsScenario' // device.feature export { DeviceJsHandledScenario } from './DeviceJsHandledScenario' @@ -80,3 +81,6 @@ export { ReactNativeErrorBoundaryScenario } from './ReactNativeErrorBoundaryScen // grouping-discriminator.feature export { GroupingDiscriminatorScenario } from './GroupingDiscriminatorScenario' export { GroupingDiscriminatorNativeScenario } from './GroupingDiscriminatorNativeScenario' + +// http_errors.feature +export { NetworkRequestScenario } from './NetworkRequestScenario' diff --git a/test/react-native/features/http_errors.feature b/test/react-native/features/http_errors.feature new file mode 100644 index 0000000000..3f59d9ce5e --- /dev/null +++ b/test/react-native/features/http_errors.feature @@ -0,0 +1,19 @@ +Feature: HTTP Errors + +Scenario Outline: Error is reported for network requests with error status code + When I run "NetworkRequestScenario" with data "" + And I wait to receive an error + Then the error payload field "events.0.context" matches the regex "^GET [0-9.]*:[0-9]{4}$" + And the error payload field "events.0.exceptions" matches the JSON fixture in "features/fixtures/expected_http_errors/.json" + Examples: + | status_code | + | 401 | + | 500 | + +Scenario Outline: Error is not reported for successful network requests + When I run "NetworkRequestScenario" with data "" + Then I should receive no errors + Examples: + | status_code | + | 200 | + | 307 | \ No newline at end of file From 7b75707be083763cf49a46dc0ca16398298cdea6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 12:24:40 +0000 Subject: [PATCH 11/52] build(deps): bump actions/setup-node from 6.1.0 to 6.2.0 (#2669) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.1.0 to 6.2.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/395ad3262231945c25e8478fd5baf05154b1d79f...6044e13b5dc448c55e2357c09f80417699197238) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/aws-lambda.yml | 2 +- .github/workflows/pr-diff.yml | 2 +- .github/workflows/test-electron.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/aws-lambda.yml b/.github/workflows/aws-lambda.yml index cde785ac26..ed9c4f3af9 100644 --- a/.github/workflows/aws-lambda.yml +++ b/.github/workflows/aws-lambda.yml @@ -29,7 +29,7 @@ jobs: ruby-version: '3.1' - name: Install Node - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 18 diff --git a/.github/workflows/pr-diff.yml b/.github/workflows/pr-diff.yml index bc9953bc01..57f45275d6 100644 --- a/.github/workflows/pr-diff.yml +++ b/.github/workflows/pr-diff.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Setup node - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 18.x diff --git a/.github/workflows/test-electron.yml b/.github/workflows/test-electron.yml index 36338b2c2e..4ca63d30de 100644 --- a/.github/workflows/test-electron.yml +++ b/.github/workflows/test-electron.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} - name: (Act) install build tools and dependencies From ffb114d95b941e5f7270e89741263d21c1951360 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 12:24:54 +0000 Subject: [PATCH 12/52] build(deps): bump ruby/setup-ruby from 1.270.0 to 1.287.0 (#2668) Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.270.0 to 1.287.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Changelog](https://github.com/ruby/setup-ruby/blob/master/release.rb) - [Commits](https://github.com/ruby/setup-ruby/compare/ac793fdd38cc468a4dd57246fa9d0e868aba9085...8d27f39a5e7ad39aebbcbd1324f7af020229645c) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-version: 1.287.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/aws-lambda.yml | 2 +- .github/workflows/update-dependencies.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/aws-lambda.yml b/.github/workflows/aws-lambda.yml index ed9c4f3af9..0777fc3b4b 100644 --- a/.github/workflows/aws-lambda.yml +++ b/.github/workflows/aws-lambda.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Install Ruby - uses: ruby/setup-ruby@ac793fdd38cc468a4dd57246fa9d0e868aba9085 # v1.270.0 + uses: ruby/setup-ruby@8d27f39a5e7ad39aebbcbd1324f7af020229645c # v1.287.0 with: ruby-version: '3.1' diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index 3fd56d17b6..e70497f131 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -40,7 +40,7 @@ jobs: - run: git submodule update --init --recursive - name: Install ruby - uses: ruby/setup-ruby@ac793fdd38cc468a4dd57246fa9d0e868aba9085 # v1.270.0 + uses: ruby/setup-ruby@8d27f39a5e7ad39aebbcbd1324f7af020229645c # v1.287.0 with: ruby-version: 2.7 From 1ac0c79710645ea3a42bd4592c5c9fe5c11cbd3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 12:25:19 +0000 Subject: [PATCH 13/52] build(deps): bump gradle/actions from 5.0.0 to 5.0.1 (#2670) Bumps [gradle/actions](https://github.com/gradle/actions) from 5.0.0 to 5.0.1. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2...f29f5a9d7b09a7c6b29859002d29d24e1674c884) --- updated-dependencies: - dependency-name: gradle/actions dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index f87dfeb0f3..a2ff507379 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -77,4 +77,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - - uses: gradle/actions/wrapper-validation@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0 + - uses: gradle/actions/wrapper-validation@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1 From 0cbf1431aa67a3b40dfe69fc2dc5c489572fad48 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 12:33:59 +0000 Subject: [PATCH 14/52] build(deps): bump actions/checkout from 6.0.1 to 6.0.2 (#2672) Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.1 to 6.0.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/8e8c483db84b4bee98b60c0593521ed34d9990e8...de0fac2e4500dabe0009e67214ff5f5447ce83dd) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/aws-lambda.yml | 2 +- .github/workflows/codeql.yml | 2 +- .github/workflows/pr-diff.yml | 4 ++-- .github/workflows/scorecard.yml | 4 ++-- .github/workflows/test-electron.yml | 2 +- .github/workflows/update-dependencies.yml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/aws-lambda.yml b/.github/workflows/aws-lambda.yml index 0777fc3b4b..6ef0a2eee0 100644 --- a/.github/workflows/aws-lambda.yml +++ b/.github/workflows/aws-lambda.yml @@ -21,7 +21,7 @@ jobs: - run: sam --version - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Ruby uses: ruby/setup-ruby@8d27f39a5e7ad39aebbcbd1324f7af020229645c # v1.287.0 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 35ab8063c9..35ab6bfb14 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -43,7 +43,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/pr-diff.yml b/.github/workflows/pr-diff.yml index 57f45275d6..d368b3a979 100644 --- a/.github/workflows/pr-diff.yml +++ b/.github/workflows/pr-diff.yml @@ -14,7 +14,7 @@ jobs: node-version: 18.x - name: Checkout base branch - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.base_ref }} @@ -29,7 +29,7 @@ jobs: cat packages/browser/dist/bugsnag.min.js | gzip | wc -c > .diff/size-before-gzipped - name: Checkout PR branch - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: clean: false diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index a2ff507379..0da1714946 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -32,7 +32,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -76,5 +76,5 @@ jobs: name: "Checksum validation of Gradle Wrappers" runs-on: ubuntu-latest steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: gradle/actions/wrapper-validation@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1 diff --git a/.github/workflows/test-electron.yml b/.github/workflows/test-electron.yml index 4ca63d30de..240dc20cd5 100644 --- a/.github/workflows/test-electron.yml +++ b/.github/workflows/test-electron.yml @@ -14,7 +14,7 @@ jobs: os: [ ubuntu-latest ] steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index e70497f131..811b44b6f5 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -28,7 +28,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} REVIEWER: gingerbenw steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: next From fdba07e33668a5c48c1bc98385ae50a39f38867a Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Thu, 5 Feb 2026 11:11:48 +0000 Subject: [PATCH 15/52] Manually parse urls (#2674) * test: :white_check_mark: fix react native test, removing need for polyfill * refactor: :recycle: update query parameter parsing * remove use of url.search * remove URLSearchParams * Find the earliest occurrence of '/', '?', or '#' to determine the domain boundary * docs: add CHANGELOG entry --- CHANGELOG.md | 1 + packages/node/test/notifier.test.ts | 1 + .../test/serverless-express.test.js | 4 ++ .../lib/extract-domain.js | 25 ++++++++++- .../lib/parse-query-params.js | 22 ++++++--- .../lib/redact-query-parameters.js | 45 +++++++------------ test/browser/features/http_errors.feature | 8 ++-- .../fixtures/expected_http_errors/401.json | 2 +- .../fixtures/expected_http_errors/500.json | 2 +- .../scenarios/core/NetworkRequestScenario.js | 4 +- 10 files changed, 68 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 286dbd0c9e..6193571f80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Changed +(plugin-network-instrumentation) Manually parse URLs to improve React Native compatibility [#2674](https://github.com/bugsnag/bugsnag-js/pull/2674) Update bugsnag-android to [v6.22.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.22.0) [#2656](https://github.com/bugsnag/bugsnag-js/pull/2656) ### Fixed diff --git a/packages/node/test/notifier.test.ts b/packages/node/test/notifier.test.ts index 1fa7cc4fed..45fb56f48f 100644 --- a/packages/node/test/notifier.test.ts +++ b/packages/node/test/notifier.test.ts @@ -4,6 +4,7 @@ describe('node notifier', () => { beforeAll(() => { jest.spyOn(console, 'debug').mockImplementation(() => {}) jest.spyOn(console, 'warn').mockImplementation(() => {}) + jest.spyOn(console, 'log').mockImplementation(() => {}) }) beforeEach(() => { diff --git a/packages/plugin-aws-lambda/test/serverless-express.test.js b/packages/plugin-aws-lambda/test/serverless-express.test.js index 0212eca34e..a39b3a30a2 100644 --- a/packages/plugin-aws-lambda/test/serverless-express.test.js +++ b/packages/plugin-aws-lambda/test/serverless-express.test.js @@ -14,6 +14,10 @@ let sentEvents let sentSessions describe('serverless express', function () { + beforeAll(() => { + jest.spyOn(console, 'debug').mockImplementation(() => {}) + }) + beforeEach(function () { sentEvents = [] sentSessions = [] diff --git a/packages/plugin-network-instrumentation/lib/extract-domain.js b/packages/plugin-network-instrumentation/lib/extract-domain.js index fd290943b5..010febc501 100644 --- a/packages/plugin-network-instrumentation/lib/extract-domain.js +++ b/packages/plugin-network-instrumentation/lib/extract-domain.js @@ -5,8 +5,29 @@ */ module.exports = function (url) { try { - const urlObj = new URL(url) - return urlObj.host + const isAbsolute = /^https?:\/\//i.test(url) + if (!isAbsolute) { + return 'unknown' + } + + const urlWithoutProtocol = url.replace(/^https?:\/\//i, '') + + // Find the earliest occurrence of '/', '?', or '#' to determine the domain boundary + const slashIndex = urlWithoutProtocol.indexOf('/') + const queryIndex = urlWithoutProtocol.indexOf('?') + const hashIndex = urlWithoutProtocol.indexOf('#') + let endIndex = urlWithoutProtocol.length + if (slashIndex !== -1 && slashIndex < endIndex) { + endIndex = slashIndex + } + if (queryIndex !== -1 && queryIndex < endIndex) { + endIndex = queryIndex + } + if (hashIndex !== -1 && hashIndex < endIndex) { + endIndex = hashIndex + } + + return urlWithoutProtocol.substring(0, endIndex) } catch (e) { return 'unknown' } diff --git a/packages/plugin-network-instrumentation/lib/parse-query-params.js b/packages/plugin-network-instrumentation/lib/parse-query-params.js index e9dc18a054..fabb4e5222 100644 --- a/packages/plugin-network-instrumentation/lib/parse-query-params.js +++ b/packages/plugin-network-instrumentation/lib/parse-query-params.js @@ -5,12 +5,24 @@ */ module.exports = function (url) { try { - const urlObj = new URL(url) - const params = {} - urlObj.searchParams.forEach((value, key) => { - params[key] = value + const queryStart = url.indexOf('?') + const hashStart = url.indexOf('#') + + let queryString = '' + if (queryStart !== -1) { + const queryEnd = hashStart !== -1 && hashStart > queryStart ? hashStart : url.length + queryString = url.substring(queryStart + 1, queryEnd) + } + + // convert query string to object without using UrlSearchParams + const queryStringObject = {} + const pairs = queryString.split('&').filter(pair => pair.length > 0) + pairs.forEach(pair => { + const [key, value] = pair.split('=') + queryStringObject[decodeURIComponent(key)] = decodeURIComponent(value || '') }) - return params + + return queryStringObject } catch (e) { return {} } diff --git a/packages/plugin-network-instrumentation/lib/redact-query-parameters.js b/packages/plugin-network-instrumentation/lib/redact-query-parameters.js index 3c69f97e08..2c36acde1f 100644 --- a/packages/plugin-network-instrumentation/lib/redact-query-parameters.js +++ b/packages/plugin-network-instrumentation/lib/redact-query-parameters.js @@ -1,38 +1,23 @@ +const parseQueryParams = require('./parse-query-params') const redactValues = require('./redact-values') -function isAbsoluteURL (url) { - try { - // eslint-disable-next-line no-new - new URL(url) - return true - } catch (e) { - return false - } -} - module.exports = function (url, redactedKeys) { - const isAbsolute = isAbsoluteURL(url) - const base = isAbsolute ? undefined : 'http://localhost' - - // Parse the URL - use a base only for relative URLs - const urlObj = new URL(url, base) - const params = new URLSearchParams(urlObj.search) - - // Convert URLSearchParams to object without using Object.fromEntries() - const paramsObject = {} - params.forEach((value, key) => { - paramsObject[key] = value - }) - + const paramsObject = parseQueryParams(url) const redactedParams = redactValues(paramsObject, redactedKeys) - urlObj.search = new URLSearchParams(redactedParams).toString() + const redactedQueryString = Object.entries(redactedParams).map(([key, value]) => `${key}=${value}`).join('&') - // Return appropriate format based on original URL type - if (isAbsolute) { - return decodeURI(urlObj.toString()) + const queryStart = url.indexOf('?') + const hashStart = url.indexOf('#') + const hash = hashStart !== -1 ? url.substring(hashStart) : '' + let result = queryStart !== -1 ? url.substring(0, queryStart) : url + + // Build the result URL manually + if (redactedQueryString && redactedQueryString.length > 0) { + result += '?' + redactedQueryString + } + if (hash) { + result += hash } - // For relative URLs, return only the path + search + hash components - const relativePart = urlObj.pathname + urlObj.search + urlObj.hash - return decodeURI(relativePart) + return result } diff --git a/test/browser/features/http_errors.feature b/test/browser/features/http_errors.feature index fef4138ee3..cdc381667f 100644 --- a/test/browser/features/http_errors.feature +++ b/test/browser/features/http_errors.feature @@ -16,7 +16,7 @@ Feature: HTTP Errors And the exception "errorClass" equals "HTTPError" And the error payload field "events.0.exceptions.0.message" equals the stored value "expected.exception.message" And the event "severity" equals "error" - And the event "unhandled" is true + And the event "unhandled" is false And the event "severityReason.type" equals "httpError" And the error payload field "events.0.context" equals the stored value "expected.context" @@ -46,7 +46,7 @@ Feature: HTTP Errors And the exception "errorClass" equals "HTTPError" And the error payload field "events.0.exceptions.0.message" equals the stored value "expected.exception.message" And the event "severity" equals "error" - And the event "unhandled" is true + And the event "unhandled" is false And the event "severityReason.type" equals "httpError" And the error payload field "events.0.context" equals the stored value "expected.context" @@ -78,7 +78,7 @@ Feature: HTTP Errors And the exception "errorClass" equals "HTTPError" And the error payload field "events.0.exceptions.0.message" equals the stored value "expected.exception.message" And the event "severity" equals "error" - And the event "unhandled" is true + And the event "unhandled" is false And the event "severityReason.type" equals "httpError" And the error payload field "events.0.context" equals the stored value "expected.context" @@ -109,7 +109,7 @@ Feature: HTTP Errors And the exception "errorClass" equals "HTTPError" And the error payload field "events.0.exceptions.0.message" equals the stored value "expected.exception.message" And the event "severity" equals "error" - And the event "unhandled" is true + And the event "unhandled" is false And the event "severityReason.type" equals "httpError" And the error payload field "events.0.context" equals the stored value "expected.context" diff --git a/test/react-native/features/fixtures/expected_http_errors/401.json b/test/react-native/features/fixtures/expected_http_errors/401.json index e719a18b29..ed32018bf4 100644 --- a/test/react-native/features/fixtures/expected_http_errors/401.json +++ b/test/react-native/features/fixtures/expected_http_errors/401.json @@ -1,6 +1,6 @@ [ { - "message": "^401: .*\/reflect\/[?]status=401$", + "message": "^401: .*\/reflect[?]status=401$", "errorClass": "HTTPError", "type": "reactnativejs", "stacktrace": "IGNORE" diff --git a/test/react-native/features/fixtures/expected_http_errors/500.json b/test/react-native/features/fixtures/expected_http_errors/500.json index ccbe396790..d65420dd36 100644 --- a/test/react-native/features/fixtures/expected_http_errors/500.json +++ b/test/react-native/features/fixtures/expected_http_errors/500.json @@ -1,6 +1,6 @@ [ { - "message": "^500: .*\/reflect\/[?]status=500$", + "message": "^500: .*\/reflect[?]status=500$", "errorClass": "HTTPError", "type": "reactnativejs", "stacktrace": "IGNORE" diff --git a/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkRequestScenario.js b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkRequestScenario.js index f69b85eb72..1470610ced 100644 --- a/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkRequestScenario.js +++ b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkRequestScenario.js @@ -18,9 +18,7 @@ export class NetworkRequestScenario extends Scenario { } run () { - const url = new URL(this.reflectEndpoint) - url.searchParams.append('status', this.statusCode) - fetch(url).catch((err) => { + fetch(`${this.reflectEndpoint}?status=${this.statusCode}`).catch((err) => { Bugsnag.notify(err) }) } From f9daf9b900ea815b55852c459e33a285ddefbe43 Mon Sep 17 00:00:00 2001 From: Bumpsnag bot <> Date: Mon, 2 Feb 2026 12:58:37 +0000 Subject: [PATCH 16/52] Update bugsnag-android to v6.23.0 [full ci] --- CHANGELOG.md | 5 ++++- packages/react-native/android/build.gradle | 4 ++-- packages/react-native/prepare-android-vendor.config | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6193571f80..e99df0e677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ Update bugsnag-android to [v6.22.0](https//github.com/bugsnag/bugsnag-android/re (plugin-network-instrumentation) Report HTTP Errors as handled [#2662](https://github.com/bugsnag/bugsnag-js/pull/2662) +### Dependencies + +Update bugsnag-android to [v6.23.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.23.0) [#2673](https://github.com/bugsnag/bugsnag-js/pull/2673) + ## [8.8.1] - 2026-01-26 ### Fixed @@ -20,7 +24,6 @@ Update bugsnag-android to [v6.22.0](https//github.com/bugsnag/bugsnag-android/re ## [8.8.0] - 2026-01-20 This release adds support for notitfying failed network requests using the new `plugin-network-instrumentation` package - ### Added (plugin-network-instrumentation) Add new plugin to notify failed network requests [#2647](https://github.com/bugsnag/bugsnag-js/pull/2647) diff --git a/packages/react-native/android/build.gradle b/packages/react-native/android/build.gradle index b3cbfbe780..552a5bbc3f 100644 --- a/packages/react-native/android/build.gradle +++ b/packages/react-native/android/build.gradle @@ -45,8 +45,8 @@ android { } dependencies { - api "com.bugsnag:bugsnag-android:6.22.0" - api "com.bugsnag:bugsnag-plugin-react-native:6.22.0" + api "com.bugsnag:bugsnag-android:6.23.0" + api "com.bugsnag:bugsnag-plugin-react-native:6.23.0" implementation 'com.facebook.react:react-native:+' testImplementation "junit:junit:4.12" diff --git a/packages/react-native/prepare-android-vendor.config b/packages/react-native/prepare-android-vendor.config index fbf5b0c027..e2dc94649a 100644 --- a/packages/react-native/prepare-android-vendor.config +++ b/packages/react-native/prepare-android-vendor.config @@ -1,2 +1,2 @@ version -6.22.0 +6.23.0 From 0d11452e5aa66620d4e3f7adc816aa60e961aeec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 03:46:04 +0000 Subject: [PATCH 17/52] build(deps): bump github/codeql-action from 4.31.8 to 4.32.2 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.8 to 4.32.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/1b168cd39490f61582a9beae412bb7057a6b2c4e...45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.32.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 6 +++--- .github/workflows/scorecard.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 35ab6bfb14..a5925fd46a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -47,7 +47,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 + uses: github/codeql-action/init@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -61,7 +61,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, 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@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 + uses: github/codeql-action/autobuild@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -74,6 +74,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 + uses: github/codeql-action/analyze@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 0da1714946..84df20ff04 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -68,7 +68,7 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4.31.8 + uses: github/codeql-action/upload-sarif@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2 with: sarif_file: results.sarif From d6d22637158cd3aef3449675ba49be668b70c527 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Feb 2026 03:46:15 +0000 Subject: [PATCH 18/52] build(deps): bump ruby/setup-ruby from 1.287.0 to 1.288.0 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.287.0 to 1.288.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Changelog](https://github.com/ruby/setup-ruby/blob/master/release.rb) - [Commits](https://github.com/ruby/setup-ruby/compare/8d27f39a5e7ad39aebbcbd1324f7af020229645c...09a7688d3b55cf0e976497ff046b70949eeaccfd) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-version: 1.288.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/aws-lambda.yml | 2 +- .github/workflows/update-dependencies.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/aws-lambda.yml b/.github/workflows/aws-lambda.yml index 6ef0a2eee0..dc959665ac 100644 --- a/.github/workflows/aws-lambda.yml +++ b/.github/workflows/aws-lambda.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Ruby - uses: ruby/setup-ruby@8d27f39a5e7ad39aebbcbd1324f7af020229645c # v1.287.0 + uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1.288.0 with: ruby-version: '3.1' diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index 811b44b6f5..c0417b79c9 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -40,7 +40,7 @@ jobs: - run: git submodule update --init --recursive - name: Install ruby - uses: ruby/setup-ruby@8d27f39a5e7ad39aebbcbd1324f7af020229645c # v1.287.0 + uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1.288.0 with: ruby-version: 2.7 From 5ebdf17f120fb29e0c24ff7efabef12468d1c29a Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Wed, 18 Feb 2026 17:09:57 +0000 Subject: [PATCH 19/52] Use Maze Runner v11 (#2680) [full ci] Co-authored-by: Steve Kirkland --- docker-compose.yml | 4 ++-- dockerfiles/Dockerfile.browser | 2 +- dockerfiles/Dockerfile.node | 2 +- test/aws-lambda/Gemfile | 2 +- test/cloudflare-workers/Gemfile | 2 +- test/node/Gemfile | 2 +- test/react-native-cli/Gemfile | 2 +- test/react-native/Gemfile | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index fbd9a2a5df..4c45dd265a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -114,7 +114,7 @@ services: - ./reports/:/app/test/node/reports/ react-native-maze-runner: - image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v10-cli + image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v11-cli environment: <<: *common-environment BITBAR_USERNAME: @@ -139,7 +139,7 @@ services: - ./reports/:/app/reports react-native-cli-maze-runner: - image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v10-cli + image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v11-cli environment: <<: *common-environment BITBAR_USERNAME: diff --git a/dockerfiles/Dockerfile.browser b/dockerfiles/Dockerfile.browser index f249209a51..509341d649 100644 --- a/dockerfiles/Dockerfile.browser +++ b/dockerfiles/Dockerfile.browser @@ -55,7 +55,7 @@ RUN find . -name package.json -type f -mindepth 2 -maxdepth 3 ! -path "./node_mo RUN rm -fr **/*/node_modules/ # The maze-runner browser tests (W3C protocol) -FROM 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v10-cli AS browser-maze-runner +FROM 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v11-cli AS browser-maze-runner COPY --from=browser-feature-builder /app/test/browser /app/test/browser/ WORKDIR /app/test/browser diff --git a/dockerfiles/Dockerfile.node b/dockerfiles/Dockerfile.node index 136416474f..afe9ee7ef4 100644 --- a/dockerfiles/Dockerfile.node +++ b/dockerfiles/Dockerfile.node @@ -22,7 +22,7 @@ RUN npm pack --verbose packages/plugin-restify/ RUN npm pack --verbose packages/plugin-hono/ # The maze-runner node tests -FROM 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v10-cli AS node-maze-runner +FROM 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v11-cli AS node-maze-runner WORKDIR /app/ COPY packages/node/ . COPY test/node/features test/node/features diff --git a/test/aws-lambda/Gemfile b/test/aws-lambda/Gemfile index 3be2e6f61c..ad600ed83d 100644 --- a/test/aws-lambda/Gemfile +++ b/test/aws-lambda/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'bugsnag-maze-runner', '~>10.0' +gem 'bugsnag-maze-runner', '~>11.0' # Use a branch of Maze Runner # gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', branch: 'tms/use-maze-check' diff --git a/test/cloudflare-workers/Gemfile b/test/cloudflare-workers/Gemfile index 3be2e6f61c..ad600ed83d 100644 --- a/test/cloudflare-workers/Gemfile +++ b/test/cloudflare-workers/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'bugsnag-maze-runner', '~>10.0' +gem 'bugsnag-maze-runner', '~>11.0' # Use a branch of Maze Runner # gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', branch: 'tms/use-maze-check' diff --git a/test/node/Gemfile b/test/node/Gemfile index a911459674..8a54de6914 100644 --- a/test/node/Gemfile +++ b/test/node/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'bugsnag-maze-runner', '~>10.0' +gem 'bugsnag-maze-runner', '~>11.0' # Use a branch of Maze Runner #gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', branch: 'tms/use-maze-check' diff --git a/test/react-native-cli/Gemfile b/test/react-native-cli/Gemfile index 44c25e95eb..76244fdbe4 100644 --- a/test/react-native-cli/Gemfile +++ b/test/react-native-cli/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' gem 'cocoapods' gem 'xcodeproj', '< 1.26.0' -gem 'bugsnag-maze-runner', '~>10.0' +gem 'bugsnag-maze-runner', '~>11.0' # Use a branch of Maze Runner #gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', branch: 'tms/use-maze-check' diff --git a/test/react-native/Gemfile b/test/react-native/Gemfile index 2c9de72294..c0d9580be5 100644 --- a/test/react-native/Gemfile +++ b/test/react-native/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' gem 'cocoapods' gem 'xcodeproj', '< 1.26.0' -gem 'bugsnag-maze-runner', '~>10.0' +gem 'bugsnag-maze-runner', '~>11.0' # Use a branch of Maze Runner #gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', branch: 'tms/use-maze-check' From 38044c866c034b53c5cc7274d711a631d910bb4c Mon Sep 17 00:00:00 2001 From: Ahmed Ahmed <74918474+hamudi-ahmed@users.noreply.github.com> Date: Wed, 25 Feb 2026 09:13:05 +0000 Subject: [PATCH 20/52] fix(plugin-network-instrumentation): omit stacktrace from HTTP Error events (#2684) * fix(plugin-network-instrumentation): omit stacktrace from HTTP Error events * docs: add changelog entry --- CHANGELOG.md | 27 ++++++++++--------- .../network-instrumentation.js | 1 + .../test/network-instrumentation-xhr.test.ts | 1 + .../test/network-instrumentation.test.ts | 1 + test/browser/features/http_errors.feature | 4 +++ 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ed786c4f9..4ea0c8ad8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,41 +4,42 @@ ### Changed -(plugin-network-instrumentation) Manually parse URLs to improve React Native compatibility [#2674](https://github.com/bugsnag/bugsnag-js/pull/2674) -Update bugsnag-android to [v6.22.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.22.0) [#2656](https://github.com/bugsnag/bugsnag-js/pull/2656) -Update bugsnag-cocoa to [v6.35.0](https//github.com/bugsnag/bugsnag-cocoa/releases/tag/v6.35.0) [#2663](https://github.com/bugsnag/bugsnag-js/pull/2663) +- (plugin-network-instrumentation) Manually parse URLs to improve React Native compatibility [#2674](https://github.com/bugsnag/bugsnag-js/pull/2674) +- Update bugsnag-android to [v6.22.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.22.0) [#2656](https://github.com/bugsnag/bugsnag-js/pull/2656) +- Update bugsnag-cocoa to [v6.35.0](https//github.com/bugsnag/bugsnag-cocoa/releases/tag/v6.35.0) [#2663](https://github.com/bugsnag/bugsnag-js/pull/2663) ### Fixed -(plugin-network-instrumentation) Report HTTP Errors as handled [#2662](https://github.com/bugsnag/bugsnag-js/pull/2662) - +- (plugin-network-instrumentation) Report HTTP Errors as handled [#2662](https://github.com/bugsnag/bugsnag-js/pull/2662) +- (plugin-network-instrumentation) Omit stacktraces from HTTP Error events [#2684](https://github.com/bugsnag/bugsnag-js/pull/2684) + ### Dependencies -Update bugsnag-android to [v6.23.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.23.0) [#2673](https://github.com/bugsnag/bugsnag-js/pull/2673) +- Update bugsnag-android to [v6.23.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.23.0) [#2673](https://github.com/bugsnag/bugsnag-js/pull/2673) ## [8.8.1] - 2026-01-26 ### Fixed -(plugin-network-breadcrumbs, plugin-network-instrumentation) Ensure XMLHttpRequest response types are handled gracefully [#2660](https://github.com/bugsnag/bugsnag-js/pull/2660) +- (plugin-network-breadcrumbs, plugin-network-instrumentation) Ensure XMLHttpRequest response types are handled gracefully [#2660](https://github.com/bugsnag/bugsnag-js/pull/2660) ## [8.8.0] - 2026-01-20 This release adds support for notitfying failed network requests using the new `plugin-network-instrumentation` package ### Added -(plugin-network-instrumentation) Add new plugin to notify failed network requests [#2647](https://github.com/bugsnag/bugsnag-js/pull/2647) -(plugin-cloudflare-workers): Add initial support for Cloudflare Workers [#2643](https://github.com/bugsnag/bugsnag-js/pull/2643) [#2644](https://github.com/bugsnag/bugsnag-js/pull/2644) -(plugin-contextualize) Return callback value from contextualize plugin [#2654](https://github.com/bugsnag/bugsnag-js/pull/2654) +- (plugin-network-instrumentation) Add new plugin to notify failed network requests [#2647](https://github.com/bugsnag/bugsnag-js/pull/2647) +- (plugin-cloudflare-workers): Add initial support for Cloudflare Workers [#2643](https://github.com/bugsnag/bugsnag-js/pull/2643) [#2644](https://github.com/bugsnag/bugsnag-js/pull/2644) +- (plugin-contextualize) Return callback value from contextualize plugin [#2654](https://github.com/bugsnag/bugsnag-js/pull/2654) ### Fixed -(plugin-server-session) Delay session tracker initialization until first use [#2642](https://github.com/bugsnag/bugsnag-js/pull/2642) +- (plugin-server-session) Delay session tracker initialization until first use [#2642](https://github.com/bugsnag/bugsnag-js/pull/2642) ### Dependencies -Update bugsnag-cocoa to [6.34.1](https//github.com/bugsnag/bugsnag-cocoa/releases/tag/6.34.1) [#2606](https://github.com/bugsnag/bugsnag-js/pull/2606) -Update bugsnag-android to [v6.20.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.20.0) [#2625](https://github.com/bugsnag/bugsnag-js/pull/2625) +- Update bugsnag-cocoa to [6.34.1](https//github.com/bugsnag/bugsnag-cocoa/releases/tag/6.34.1) [#2606](https://github.com/bugsnag/bugsnag-js/pull/2606) +- Update bugsnag-android to [v6.20.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.20.0) [#2625](https://github.com/bugsnag/bugsnag-js/pull/2625) ## [8.7.0] - 2025-10-13 diff --git a/packages/plugin-network-instrumentation/network-instrumentation.js b/packages/plugin-network-instrumentation/network-instrumentation.js index 530edb9fef..009b8651dd 100644 --- a/packages/plugin-network-instrumentation/network-instrumentation.js +++ b/packages/plugin-network-instrumentation/network-instrumentation.js @@ -148,6 +148,7 @@ module.exports = (config = {}, global = window) => { 0 ) + event.errors[0].stacktrace = [] // Omit the stacktrace for HTTP errors event.request = requestObj event.response = responseObj event.context = `${method} ${domain}` diff --git a/packages/plugin-network-instrumentation/test/network-instrumentation-xhr.test.ts b/packages/plugin-network-instrumentation/test/network-instrumentation-xhr.test.ts index 63f28e4e90..f9c64a2825 100644 --- a/packages/plugin-network-instrumentation/test/network-instrumentation-xhr.test.ts +++ b/packages/plugin-network-instrumentation/test/network-instrumentation-xhr.test.ts @@ -133,6 +133,7 @@ describe('plugin-network-instrumentation', () => { // Verify error details expect(event.exceptions[0].errorClass).toBe('HTTPError') expect(event.exceptions[0].errorMessage).toBe('404: https://api.example.com/users/123') + expect(event.exceptions[0].stacktrace).toEqual([]) // Stacktrace should be empty for HTTP errors expect(event.context).toBe('POST api.example.com') expect(event.severity).toBe('error') expect(event.unhandled).toBe(false) diff --git a/packages/plugin-network-instrumentation/test/network-instrumentation.test.ts b/packages/plugin-network-instrumentation/test/network-instrumentation.test.ts index fda8520bfd..e8aba988d0 100644 --- a/packages/plugin-network-instrumentation/test/network-instrumentation.test.ts +++ b/packages/plugin-network-instrumentation/test/network-instrumentation.test.ts @@ -85,6 +85,7 @@ describe('plugin-network-instrumentation', () => { expect(event.exceptions[0].errorClass).toBe('HTTPError') expect(event.exceptions[0].errorMessage).toBe('404: https://example.com/api/users') + expect(event.exceptions[0].stacktrace).toEqual([]) // Stacktrace should be empty for HTTP errors expect(event.context).toBe('GET example.com') expect(event.severity).toBe('error') expect(event.unhandled).toBe(false) diff --git a/test/browser/features/http_errors.feature b/test/browser/features/http_errors.feature index cdc381667f..4041b00c91 100644 --- a/test/browser/features/http_errors.feature +++ b/test/browser/features/http_errors.feature @@ -15,6 +15,7 @@ Feature: HTTP Errors And the exception "errorClass" equals "HTTPError" And the error payload field "events.0.exceptions.0.message" equals the stored value "expected.exception.message" + And the error payload field "events.0.exceptions.0.stacktrace" is an array with 0 elements And the event "severity" equals "error" And the event "unhandled" is false And the event "severityReason.type" equals "httpError" @@ -45,6 +46,7 @@ Feature: HTTP Errors And the exception "errorClass" equals "HTTPError" And the error payload field "events.0.exceptions.0.message" equals the stored value "expected.exception.message" + And the error payload field "events.0.exceptions.0.stacktrace" is an array with 0 elements And the event "severity" equals "error" And the event "unhandled" is false And the event "severityReason.type" equals "httpError" @@ -77,6 +79,7 @@ Feature: HTTP Errors And the exception "errorClass" equals "HTTPError" And the error payload field "events.0.exceptions.0.message" equals the stored value "expected.exception.message" + And the error payload field "events.0.exceptions.0.stacktrace" is an array with 0 elements And the event "severity" equals "error" And the event "unhandled" is false And the event "severityReason.type" equals "httpError" @@ -108,6 +111,7 @@ Feature: HTTP Errors And the exception "errorClass" equals "HTTPError" And the error payload field "events.0.exceptions.0.message" equals the stored value "expected.exception.message" + And the error payload field "events.0.exceptions.0.stacktrace" is an array with 0 elements And the event "severity" equals "error" And the event "unhandled" is false And the event "severityReason.type" equals "httpError" From 071bfc56e04bfe55315bfbae5a4aad32c6c3e644 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 03:45:21 +0000 Subject: [PATCH 21/52] build(deps): bump actions/upload-artifact from 6.0.0 to 7.0.0 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/b7c566a772e6b6bfb58ed0dc250532a479d7789f...bbbca2ddaa5d8feaa63e36b76fdaad77386f024f) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/scorecard.yml | 2 +- .github/workflows/test-electron.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 84df20ff04..a2894a84c8 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -59,7 +59,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: SARIF file path: results.sarif diff --git a/.github/workflows/test-electron.yml b/.github/workflows/test-electron.yml index 240dc20cd5..f4d42c53a6 100644 --- a/.github/workflows/test-electron.yml +++ b/.github/workflows/test-electron.yml @@ -66,7 +66,7 @@ jobs: START_LOCAL_NPM: 1 VERBOSE: 1 ELECTRON_VERSION: ${{ matrix.electron }} - - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 if: failure() with: name: cucumber-failures From 19d376f9e088e357ff1a248dabc41d3dd9f251ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 03:45:25 +0000 Subject: [PATCH 22/52] build(deps): bump gradle/actions from 5.0.1 to 5.0.2 Bumps [gradle/actions](https://github.com/gradle/actions) from 5.0.1 to 5.0.2. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/f29f5a9d7b09a7c6b29859002d29d24e1674c884...0723195856401067f7a2779048b490ace7a47d7c) --- updated-dependencies: - dependency-name: gradle/actions dependency-version: 5.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 84df20ff04..3eaff0a3e7 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -77,4 +77,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: gradle/actions/wrapper-validation@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1 + - uses: gradle/actions/wrapper-validation@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2 From f054769fcca9bf41010e46bf92f17f4efd4d072f Mon Sep 17 00:00:00 2001 From: Bumpsnag bot <> Date: Mon, 2 Mar 2026 17:04:14 +0000 Subject: [PATCH 23/52] Update bugsnag-android to v6.25.0 [full ci] --- CHANGELOG.md | 3 ++- packages/react-native/android/build.gradle | 4 ++-- packages/react-native/prepare-android-vendor.config | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ea0c8ad8e..84df4b9011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,11 @@ - (plugin-network-instrumentation) Report HTTP Errors as handled [#2662](https://github.com/bugsnag/bugsnag-js/pull/2662) - (plugin-network-instrumentation) Omit stacktraces from HTTP Error events [#2684](https://github.com/bugsnag/bugsnag-js/pull/2684) - + ### Dependencies - Update bugsnag-android to [v6.23.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.23.0) [#2673](https://github.com/bugsnag/bugsnag-js/pull/2673) +Update bugsnag-android to [v6.25.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.25.0) [#2689](https://github.com/bugsnag/bugsnag-js/pull/2689) ## [8.8.1] - 2026-01-26 diff --git a/packages/react-native/android/build.gradle b/packages/react-native/android/build.gradle index 552a5bbc3f..fdf534e4a1 100644 --- a/packages/react-native/android/build.gradle +++ b/packages/react-native/android/build.gradle @@ -45,8 +45,8 @@ android { } dependencies { - api "com.bugsnag:bugsnag-android:6.23.0" - api "com.bugsnag:bugsnag-plugin-react-native:6.23.0" + api "com.bugsnag:bugsnag-android:6.25.0" + api "com.bugsnag:bugsnag-plugin-react-native:6.25.0" implementation 'com.facebook.react:react-native:+' testImplementation "junit:junit:4.12" diff --git a/packages/react-native/prepare-android-vendor.config b/packages/react-native/prepare-android-vendor.config index e2dc94649a..5d36d1f248 100644 --- a/packages/react-native/prepare-android-vendor.config +++ b/packages/react-native/prepare-android-vendor.config @@ -1,2 +1,2 @@ version -6.23.0 +6.25.0 From 0283dfece50807eb38a15096fd2b0daa70834553 Mon Sep 17 00:00:00 2001 From: Ahmed Ahmed <74918474+hamudi-ahmed@users.noreply.github.com> Date: Tue, 3 Mar 2026 12:29:12 +0000 Subject: [PATCH 24/52] chore: update verdaccio config and react native development guide (#2690) * chore: proxy @bugsnag/cli in verdaccio config * chore: move verdaccio config to test directory * chore: update dev instructions in React Native contributing guide --- package.json | 2 +- packages/react-native/CONTRIBUTING.md | 41 +++++++++++------------- test/{electron => }/local-npm-config.yml | 3 ++ 3 files changed, 23 insertions(+), 23 deletions(-) rename test/{electron => }/local-npm-config.yml (90%) diff --git a/package.json b/package.json index 84964698f9..760986226d 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "test:build-node-container": "docker compose up --build minimal-packager && docker compose build --pull node-maze-runner", "test:build-react-native-maze-runner": "docker-compose build --pull react-native-maze-runner", "test:node": "npm run test:build-node-container && docker compose run --use-aliases node-maze-runner", - "local-npm:start": "verdaccio --config test/electron/local-npm-config.yml --listen 0.0.0.0:5539", + "local-npm:start": "verdaccio --config test/local-npm-config.yml --listen 0.0.0.0:5539", "local-npm:publish-all": "lerna publish \"$VERSION_IDENTIFIER\" --yes --force-publish --exact --no-push --no-git-reset --no-git-tag-version --registry 'http://0.0.0.0:5539'", "local-npm:publish-all-win32": "lerna publish %VERSION_IDENTIFIER% --yes --force-publish --exact --no-push --no-git-reset --no-git-tag-version --registry 'http://0.0.0.0:5539'", "test:electron": "xvfb-maybe --auto-servernum -- cucumber-js test/electron/features --retry 3 --retry-tag-filter @flaky" diff --git a/packages/react-native/CONTRIBUTING.md b/packages/react-native/CONTRIBUTING.md index 30ce0823ca..42117060e4 100644 --- a/packages/react-native/CONTRIBUTING.md +++ b/packages/react-native/CONTRIBUTING.md @@ -37,46 +37,43 @@ To solve this problem we publish to a local npm clone, which proxies requests fo #### Prerequisites -The proxy of choice is [verdaccio](https://verdaccio.org/): +The proxy of choice is [verdaccio](https://verdaccio.org/). This is already included as a dev dependency in the bugsnag-js repository, along with a config file at `test/local-npm.config.yml`. -```sh -# install it globally on your system -npm i -g verdaccio +To start the verdaccio server, run the `local-npm:start` npm script from the repo root: -# starts the on the default port -verdaccio - -# log in to the registry -# (you can enter anything, just be sure to remember them when -# your session times out and you need to "sign in" again) -npm adduser --registry http://localhost:4873 ``` +npm run local-npm:start +``` + +This will start verdaccio running on port `5539`. You will need to keep this running for the following steps. -On the project you want to install the development notifier, create an a `.npmrc` file at the project root alongside `package.json`: +In the project where you want to install the development notifier, create an a `.npmrc` file at the project root alongside `package.json` and set the local registry URL: ``` -registry=http://localhost:4873 +registry=http://localhost:5539 ``` -Alternatively you can just supply the `--registry=http://localhost:4873` to each npm/yarn command you issue. +Alternatively you can just supply the `--registry=http://localhost:5539` to each npm/yarn command you issue. -#### Installing the development notifier on a React Native project +#### Installing the development notifier in a React Native project 1. Make changes. -2. Run the following command to publish to the local registry: +2. In a new terminal window, from the repo root, run the `local-npm:publish-all` npm script to publish to the local registry: ``` - npx lerna publish v99.99.99-canary.`git rev-parse HEAD` --no-push --exact --no-git-tag-version --registry http://localhost:4873/ + VERSION_IDENTIFIER=8.99.99 npm run local-npm:publish-all ``` - This should prompt you for each module that has changed since the last proper publish. + This will publish all of the packages in the repo to verdaccio with the specified version. + + Note: You'll need to ensure you publish using the same major version as is currently in the repository. This is because some packages declare a peer dependency on `@bugsnag/core`, and lerna does not update peer dependencies when versioning, so changing the major version will mean the packages fail to install (since the peer dependency cannot be resolved from the local registry). -4. Reset the changes that were made to `lerna.json` and `package-lock.json`s `git reset --hard HEAD` (we don't want to store these throwaway versions) +4. Reset the changes that were made to `package.json`, `lerna.json` and `package-lock.json` files with `git reset --hard HEAD` (we don't want to commit these throwaway versions) -On the project you want to install `@bugsnag/react-native` substitute the version's output from above: +In the project where you want to install `@bugsnag/react-native` substitute the version's output from above: ``` -yarn add @bugsnag/react-native@99.99.99-canary. +yarn add @bugsnag/react-native@8.99.99 # or -npm i @bugsnag/react-native@99.99.99-canary. +npm i @bugsnag/react-native@8.99.99 ``` diff --git a/test/electron/local-npm-config.yml b/test/local-npm-config.yml similarity index 90% rename from test/electron/local-npm-config.yml rename to test/local-npm-config.yml index 4ae36c6d2b..fb30d19cb4 100644 --- a/test/electron/local-npm-config.yml +++ b/test/local-npm-config.yml @@ -13,6 +13,9 @@ packages: '@bugsnag/cuid': access: $anonymous proxy: npmjs + '@bugsnag/cli': + access: $anonymous + proxy: npmjs 'bugsnag-expo-cli': access: $anonymous publish: $anonymous From 843442f5cf1b2c7c71b2a95d9c143d6f805d425d Mon Sep 17 00:00:00 2001 From: Ahmed Ahmed <74918474+hamudi-ahmed@users.noreply.github.com> Date: Tue, 3 Mar 2026 15:43:46 +0000 Subject: [PATCH 25/52] fix(react-native): report error cause with native stacktrace for turbo module exceptions (#2686) * feat(react-native): native stacktraces for turbo module exceptions * test(react-native): add e2e tests for turbo module mixed stacktraces * docs: add changelog entry --- CHANGELOG.md | 6 +- packages/delivery-react-native/delivery.js | 26 +++- .../test/delivery.test.ts | 102 +++++++++++++- .../BugsnagEventDeserializer.m | 24 ++-- ...k.feature => native-stack-android.feature} | 131 ++++++------------ .../features/native-stack-ios.feature | 116 ++++++++++++++++ 6 files changed, 302 insertions(+), 103 deletions(-) rename test/react-native/features/{native-stack.feature => native-stack-android.feature} (51%) create mode 100644 test/react-native/features/native-stack-ios.feature diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ea0c8ad8e..3a87a613eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,16 +5,17 @@ ### Changed - (plugin-network-instrumentation) Manually parse URLs to improve React Native compatibility [#2674](https://github.com/bugsnag/bugsnag-js/pull/2674) -- Update bugsnag-android to [v6.22.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.22.0) [#2656](https://github.com/bugsnag/bugsnag-js/pull/2656) -- Update bugsnag-cocoa to [v6.35.0](https//github.com/bugsnag/bugsnag-cocoa/releases/tag/v6.35.0) [#2663](https://github.com/bugsnag/bugsnag-js/pull/2663) ### Fixed - (plugin-network-instrumentation) Report HTTP Errors as handled [#2662](https://github.com/bugsnag/bugsnag-js/pull/2662) - (plugin-network-instrumentation) Omit stacktraces from HTTP Error events [#2684](https://github.com/bugsnag/bugsnag-js/pull/2684) +- (react-native) Report error cause with native stacktrace for Turbo Module exceptions [#2686] (https://github.com/bugsnag/bugsnag-js/pull/2686) ### Dependencies +- Update bugsnag-cocoa to [v6.35.0](https//github.com/bugsnag/bugsnag-cocoa/releases/tag/v6.35.0) [#2663](https://github.com/bugsnag/bugsnag-js/pull/2663) +- Update bugsnag-android to [v6.22.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.22.0) [#2656](https://github.com/bugsnag/bugsnag-js/pull/2656) - Update bugsnag-android to [v6.23.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.23.0) [#2673](https://github.com/bugsnag/bugsnag-js/pull/2673) ## [8.8.1] - 2026-01-26 @@ -26,6 +27,7 @@ ## [8.8.0] - 2026-01-20 This release adds support for notitfying failed network requests using the new `plugin-network-instrumentation` package + ### Added - (plugin-network-instrumentation) Add new plugin to notify failed network requests [#2647](https://github.com/bugsnag/bugsnag-js/pull/2647) diff --git a/packages/delivery-react-native/delivery.js b/packages/delivery-react-native/delivery.js index 49561f9061..97c3e86d2e 100644 --- a/packages/delivery-react-native/delivery.js +++ b/packages/delivery-react-native/delivery.js @@ -3,12 +3,35 @@ const derecursify = require('@bugsnag/core/lib/derecursify') module.exports = (client, NativeClient) => ({ sendEvent: (payload, cb = () => {}) => { const event = payload.events[0] - let nativeStack + if (event.originalError) { + // extract native stacktrace from originalError if available + let nativeErrorMessage, nativeStack if (event.originalError.nativeStackIOS) { + // old arch ios + nativeErrorMessage = event.originalError.message nativeStack = event.originalError.nativeStackIOS } else if (event.originalError.nativeStackAndroid) { + // old arch android + nativeErrorMessage = event.originalError.message nativeStack = event.originalError.nativeStackAndroid + } else if (event.originalError.cause && event.originalError.cause.stackSymbols) { + // new arch ios + nativeErrorMessage = event.originalError.cause.message + nativeStack = event.originalError.cause.stackSymbols + } else if (event.originalError.cause && event.originalError.cause.stackElements) { + // new arch android + nativeErrorMessage = event.originalError.cause.message + nativeStack = event.originalError.cause.stackElements + } + + if (nativeErrorMessage && nativeStack) { + // add the native stack to the corresponding error in the event payload + const nativeError = event.errors.find(err => err.errorMessage === nativeErrorMessage) + + if (nativeError) { + nativeError.nativeStack = nativeStack + } } } @@ -30,7 +53,6 @@ module.exports = (client, NativeClient) => ({ groupingDiscriminator: event._groupingDiscriminator, apiKey: event.apiKey, featureFlags: event.toJSON().featureFlags, - nativeStack: nativeStack, correlation: event._correlation } diff --git a/packages/delivery-react-native/test/delivery.test.ts b/packages/delivery-react-native/test/delivery.test.ts index 27d847cc2d..109badb23f 100644 --- a/packages/delivery-react-native/test/delivery.test.ts +++ b/packages/delivery-react-native/test/delivery.test.ts @@ -31,9 +31,17 @@ type NativeClientEvent = Pick { @@ -111,7 +119,8 @@ describe('delivery: react native', () => { ] c.notify(error, (e) => {}, (err, event) => { expect(err).not.toBeTruthy() - expect(sent[0].nativeStack).toEqual(error.nativeStackIOS) + // @ts-expect-error nativeStack is added by the delivery module + expect(sent[0].errors[0].nativeStack).toEqual(error.nativeStackIOS) done() }) }) @@ -137,7 +146,96 @@ describe('delivery: react native', () => { ] c.notify(error, (e) => {}, (err, event) => { expect(err).not.toBeTruthy() - expect(sent[0].nativeStack).toBe(error.nativeStackAndroid) + // @ts-expect-error nativeStack is added by the delivery module + expect(sent[0].errors[0].nativeStack).toBe(error.nativeStackAndroid) + done() + }) + }) + + it('extracts iOS native error cause', done => { + const sent: NativeClientEvent[] = [] + const NativeClient = { + dispatchAsync: (event: NativeClientEvent) => { + sent.push(event) + return new Promise((resolve) => resolve(true)) + } + } + const c = new Client({ apiKey: 'api_key' }) + c._setDelivery(client => delivery(client, NativeClient)) + const error = new ReactNativeError('oh no') + const stackSymbols = [ + '0 ReactNativeTest 0x000000010fda7f1b RCTJSErrorFromCodeMessageAndNSError + 79', + '1 ReactNativeTest 0x000000010fd76897 __41-[RCTModuleMethod processMethodSignature]_block_invoke_2.103 + 97', + '2 ReactNativeTest 0x000000010fccd9c3 -[BenCrash asyncReject:rejecter:] + 106', + '3 CoreFoundation 0x00007fff23e44dec __invoking___ + 140', + '4 CoreFoundation 0x00007fff23e41fd1 -[NSInvocation invoke] + 321', + '5 CoreFoundation 0x00007fff23e422a4 -[NSInvocation invokeWithTarget:] + 68', + '6 ReactNativeTest 0x000000010fd76eae -[RCTModuleMethod invokeWithBridge:module:arguments:] + 578', + '7 ReactNativeTest 0x000000010fd79138 _ZN8facebook5reactL11invokeInnerEP9RCTBridgeP13RCTModuleDatajRKN5folly7dynamicE + 246' + ] + + error.cause = { + name: 'NativeiOSException', + message: 'Native iOS error occurred', + stackSymbols + } + + c.notify(error, (e) => {}, (err, event) => { + expect(err).not.toBeTruthy() + expect(sent[0].errors[0].errorMessage).toEqual(error.message) + // @ts-expect-error nativeStack is added by the delivery module + expect(sent[0].errors[0].nativeStack).toBeUndefined() + + expect(sent[0].errors[1].errorMessage).toBeDefined() + expect(sent[0].errors[1].errorMessage).toEqual(error.cause?.message) + expect(sent[0].errors[1].errorClass).toBeDefined() + expect(sent[0].errors[1].errorClass).toEqual(error.cause?.name) + + // @ts-expect-error nativeStack is added by the delivery module + expect(sent[0].errors[1].nativeStack).toEqual(error.cause.stackSymbols) + done() + }) + }) + + it('extracts Android native error cause', done => { + const sent: NativeClientEvent[] = [] + const NativeClient = { + dispatchAsync: (event: NativeClientEvent) => { + sent.push(event) + return new Promise((resolve) => resolve(true)) + } + } + const c = new Client({ apiKey: 'api_key' }) + c._setDelivery(client => delivery(client, NativeClient)) + const error = new ReactNativeError('oh no') + const stackElements = [ + { + class: 'com.testing.Blah', + lineNumber: 101, + file: 'app/com.testing.Blah.java', + methodName: 'crash()' + } + ] + + error.cause = { + name: 'NativeAndroidException', + message: 'Native Android error occurred', + stackElements + } + + c.notify(error, (e) => {}, (err, event) => { + expect(err).not.toBeTruthy() + expect(sent[0].errors[0].errorMessage).toEqual(error.message) + // @ts-expect-error nativeStack is added by the delivery module + expect(sent[0].errors[0].nativeStack).toBeUndefined() + + expect(sent[0].errors[1].errorMessage).toBeDefined() + expect(sent[0].errors[1].errorMessage).toEqual(error.cause?.message) + expect(sent[0].errors[1].errorClass).toBeDefined() + expect(sent[0].errors[1].errorClass).toEqual(error.cause?.name) + + // @ts-expect-error nativeStack is added by the delivery module + expect(sent[0].errors[1].nativeStack).toEqual(error.cause.stackElements) done() }) }) diff --git a/packages/react-native/ios/BugsnagReactNative/BugsnagEventDeserializer.m b/packages/react-native/ios/BugsnagReactNative/BugsnagEventDeserializer.m index 38547b6f4e..5c9f28c535 100644 --- a/packages/react-native/ios/BugsnagReactNative/BugsnagEventDeserializer.m +++ b/packages/react-native/ios/BugsnagReactNative/BugsnagEventDeserializer.m @@ -9,6 +9,7 @@ #import "BugsnagEventDeserializer.h" #import "BugsnagInternals.h" +#import @implementation BugsnagEventDeserializer @@ -25,7 +26,7 @@ - (BugsnagEvent *)deserializeEvent:(NSDictionary *)payload { user:[[BugsnagUser alloc] initWithDictionary:user] metadata:metadata breadcrumbs:[self deserializeBreadcrumbs:payload[@"breadcrumbs"]] - errors:@[[BugsnagError new]] + errors:[self deserializeErrors:payload[@"errors"]] threads:[self deserializeThreads:payload[@"threads"]] session:nil /* set by -[BugsnagClient notifyInternal:block:] */]; event.context = payload[@"context"]; @@ -49,13 +50,17 @@ - (BugsnagEvent *)deserializeEvent:(NSDictionary *)payload { } } - NSDictionary *error = payload[@"errors"][0]; + return event; +} - if (error != nil) { - event.errors[0].errorClass = error[@"errorClass"]; - event.errors[0].errorMessage = error[@"errorMessage"]; +- (NSArray *)deserializeErrors:(NSArray *)errors { + NSMutableArray *array = [NSMutableArray new]; + for (NSDictionary *error in errors) { + BugsnagError *errorObj = [BugsnagError new]; + errorObj.errorClass = error[@"errorClass"]; + errorObj.errorMessage = error[@"errorMessage"]; NSArray *stacktrace = error[@"stacktrace"]; - NSArray *nativeStack = payload[@"nativeStack"]; + NSArray *nativeStack = error[@"nativeStack"]; if (nativeStack) { NSMutableArray *mixedStack = [NSMutableArray array]; for (BugsnagStackframe *frame in [BugsnagStackframe stackframesWithCallStackSymbols:nativeStack]) { @@ -66,10 +71,13 @@ - (BugsnagEvent *)deserializeEvent:(NSDictionary *)payload { [mixedStack addObjectsFromArray:stacktrace]; stacktrace = mixedStack; } - [event attachCustomStacktrace:stacktrace withType:@"reactnativejs"]; + + errorObj.stacktrace = [BugsnagStacktrace stacktraceFromJson:stacktrace].trace; + errorObj.type = BSGErrorTypeReactNativeJs; + [array addObject:errorObj]; } - return event; + return array; } - (NSArray *)deserializeBreadcrumbs:(NSArray *)crumbs { diff --git a/test/react-native/features/native-stack.feature b/test/react-native/features/native-stack-android.feature similarity index 51% rename from test/react-native/features/native-stack.feature rename to test/react-native/features/native-stack-android.feature index f2e6651b1e..8d5cb09177 100644 --- a/test/react-native/features/native-stack.feature +++ b/test/react-native/features/native-stack-android.feature @@ -1,7 +1,8 @@ +@android_only Feature: Native stacktrace is parsed for promise rejections # Skipped on New Arch below 0.74 - see PLAT-12193 -@android_only @skip_new_arch_below_074 +@skip_new_arch_below_074 Scenario: Handled native promise rejection with native stacktrace When I run "NativePromiseRejectionHandledScenario" Then I wait to receive an error @@ -11,7 +12,6 @@ Scenario: Handled native promise rejection with native stacktrace # On 0.75+ the Error name is set to the native exception class And the event "exceptions.0.errorClass" equals the version-dependent string: | arch | version | value | - | new | 0.72 | Error | | new | 0.74 | Error | | new | default | java.lang.RuntimeException | | old | 0.68 | Error | @@ -41,14 +41,14 @@ Scenario: Handled native promise rejection with native stacktrace | runScenario | # the javascript part follows - # On 0.74+ New Arch there is no JS stacktrace - see PLAT-12193 + # On New Arch there is no JS stacktrace - see PLAT-12193 And the stacktrace contains "file" equal to the version-dependent string: | arch | version | value | | new | default | @skip | | old | default | index.android.bundle | # Skipped on New Arch below 0.74 - see PLAT-12193 -@android_only @skip_new_arch_below_074 +@skip_new_arch_below_074 Scenario: Unhandled native promise rejection with native stacktrace When I run "NativePromiseRejectionUnhandledScenario" Then I wait to receive an error @@ -57,7 +57,6 @@ Scenario: Unhandled native promise rejection with native stacktrace # On 0.75+ the Error name is set to the native exception class And the event "exceptions.0.errorClass" equals the version-dependent string: | arch | version | value | - | new | 0.72 | Error | | new | 0.74 | Error | | new | default | java.lang.RuntimeException | | old | 0.68 | Error | @@ -94,7 +93,7 @@ Scenario: Unhandled native promise rejection with native stacktrace | runScenario | # the javascript part follows - # On 0.74+ New Arch there is no JS stacktrace - see PLAT-12193 + # On New Arch there is no JS stacktrace - see PLAT-12193 And the stacktrace contains "file" equal to the version-dependent string: | arch | version | value | | new | default | @skip | @@ -105,93 +104,47 @@ Scenario: Unhandled native promise rejection with native stacktrace # And the error payload field "events.0.exceptions.1.stacktrace.1.lineNumber" equals 1 # And the error payload field "events.0.exceptions.1.stacktrace.2.lineNumber" equals 2 -# Skipped on New Arch below 0.74 - see PLAT-12193 -@ios_only @skip_new_arch_below_074 -Scenario: Handled native promise rejection with native stacktrace - When I run "NativePromiseRejectionHandledScenario" +@skip_old_arch @skip_new_arch_below_074 +Scenario: Unhandled exception in synchronous turbo module method with native stacktrace + When I run "UnhandledNativeErrorSyncScenario" and relaunch the crashed app + And I configure Bugsnag for "UnhandledNativeErrorSyncScenario" Then I wait to receive an error - And the event "unhandled" is false - And the error payload field "events.0.exceptions" is an array with 1 elements + And the event "unhandled" is true + + # First exception is the JS Error with JS stacktrace And the event "exceptions.0.errorClass" equals "Error" - And the event "exceptions.0.message" equals "NativePromiseRejectionHandledScenario" + And the event "exceptions.0.message" equals "Exception in HostFunction: UnhandledNativeErrorScenario" And the event "exceptions.0.type" equals "reactnativejs" - And the error payload field "events.0.exceptions.0.stacktrace" is a non-empty array - - # the native part of the stack comes first - And the error payload field "events.0.exceptions.0.stacktrace.0.frameAddress" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.machoFile" equals "reactnative" - And the error payload field "events.0.exceptions.0.stacktrace.0.machoLoadAddress" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.machoUUID" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.machoVMAddress" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.method" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.symbolAddress" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.type" equals "cocoa" - - # the javascript part follows - # On 0.74+ New Arch there is no JS stacktrace - see PLAT-12193 - # We're check the method: asyncGeneratorStep - And the event "exceptions.0.stacktrace.21.columnNumber" equals the version-dependent string: - | arch | version | value | - | new | 0.72 | @not_null | - | new | default | @skip | - | old | default | @not_null | - And the event "exceptions.0.stacktrace.21.file" equals the version-dependent string: - | arch | version | value | - | new | 0.72 | @not_null | - | new | default | @skip | - | old | default | @not_null | - And the event "exceptions.0.stacktrace.21.lineNumber" equals the version-dependent string: - | arch | version | value | - | new | 0.72 | @not_null | - | new | default | @skip | - | old | default | @not_null | - And the event "exceptions.0.stacktrace.21.type" equals the version-dependent string: - | arch | version | value | - | new | 0.72 | @null | - | new | default | @skip | - | old | default | @null | - -# Skipped on New Arch below 0.74 - see PLAT-12193 -@ios_only @skip_new_arch_below_074 -Scenario: Unhandled native promise rejection with native stacktrace - When I run "NativePromiseRejectionUnhandledScenario" + And the event "exceptions.0.stacktrace.0.method" equals "runScenarioSync" + And the event "exceptions.0.stacktrace.0.file" equals "(native)" + And the event "exceptions.0.stacktrace.1.method" equals "run" + And the event "exceptions.0.stacktrace.1.file" equals "index.android.bundle" + + # Second exception (cause) is the native exception with native stacktrace + And the event "exceptions.1.errorClass" equals "java.lang.RuntimeException" + And the event "exceptions.1.message" equals "UnhandledNativeErrorScenario" + And the event "exceptions.1.type" equals "reactnativejs" + And the event "exceptions.1.stacktrace.0.method" equals "com.reactnative.scenarios.Scenario.generateException" + And the event "exceptions.1.stacktrace.0.file" equals "Scenario.kt" + And the event "exceptions.1.stacktrace.0.type" equals "android" + +@skip_old_arch @skip_new_arch_below_074 +Scenario: Unhandled exception in asynchronous turbo module method with native stacktrace + When I run "UnhandledNativeErrorScenario" and relaunch the crashed app + And I configure Bugsnag for "UnhandledNativeErrorScenario" Then I wait to receive an error And the event "unhandled" is true + + # First exception is the JS Error, however there is no JS stacktrace And the event "exceptions.0.errorClass" equals "Error" - And the event "exceptions.0.message" equals "NativePromiseRejectionUnhandledScenario" + And the event "exceptions.0.message" equals "Exception in HostFunction: UnhandledNativeErrorScenario" And the event "exceptions.0.type" equals "reactnativejs" - And the error payload field "events.0.exceptions.0.stacktrace" is a non-empty array - - # the native part of the stack comes first - And the error payload field "events.0.exceptions.0.stacktrace.0.frameAddress" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.machoFile" equals "reactnative" - And the error payload field "events.0.exceptions.0.stacktrace.0.machoLoadAddress" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.machoUUID" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.machoVMAddress" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.method" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.symbolAddress" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.type" equals "cocoa" - - # the javascript part follows - # On 0.74+ New Arch there is no JS stacktrace - see PLAT-12193 - # We're check the method: asyncGeneratorStep - And the event "exceptions.0.stacktrace.21.columnNumber" equals the version-dependent string: - | arch | version | value | - | new | 0.72 | @not_null | - | new | default | @skip | - | old | default | @not_null | - And the event "exceptions.0.stacktrace.21.file" equals the version-dependent string: - | arch | version | value | - | new | 0.72 | @not_null | - | new | default | @skip | - | old | default | @not_null | - And the event "exceptions.0.stacktrace.21.lineNumber" equals the version-dependent string: - | arch | version | value | - | new | 0.72 | @not_null | - | new | default | @skip | - | old | default | @not_null | - And the event "exceptions.0.stacktrace.21.type" equals the version-dependent string: - | arch | version | value | - | new | 0.72 | @null | - | new | default | @skip | - | old | default | @null | + And the error payload field "events.0.exceptions.0.stacktrace" is an array with 0 elements + + # Second exception (cause) is the native exception with native stacktrace + And the event "exceptions.1.errorClass" equals "java.lang.RuntimeException" + And the event "exceptions.1.message" equals "UnhandledNativeErrorScenario" + And the event "exceptions.1.type" equals "reactnativejs" + And the event "exceptions.1.stacktrace.0.method" equals "com.reactnative.scenarios.Scenario.generateException" + And the event "exceptions.1.stacktrace.0.file" equals "Scenario.kt" + And the event "exceptions.1.stacktrace.0.type" equals "android" diff --git a/test/react-native/features/native-stack-ios.feature b/test/react-native/features/native-stack-ios.feature new file mode 100644 index 0000000000..0f9228007c --- /dev/null +++ b/test/react-native/features/native-stack-ios.feature @@ -0,0 +1,116 @@ +@ios_only +Feature: Native stacktrace is parsed for promise rejections + +# Skipped on New Arch below 0.74 - see PLAT-12193 +@skip_new_arch_below_074 +Scenario: Handled native promise rejection with native stacktrace + When I run "NativePromiseRejectionHandledScenario" + Then I wait to receive an error + And the event "unhandled" is false + And the error payload field "events.0.exceptions" is an array with 1 elements + And the event "exceptions.0.errorClass" equals "Error" + And the event "exceptions.0.message" equals "NativePromiseRejectionHandledScenario" + And the event "exceptions.0.type" equals "reactnativejs" + And the error payload field "events.0.exceptions.0.stacktrace" is a non-empty array + + # the native part of the stack comes first + And the error payload field "events.0.exceptions.0.stacktrace.0.frameAddress" is not null + And the error payload field "events.0.exceptions.0.stacktrace.0.machoFile" equals "reactnative" + And the error payload field "events.0.exceptions.0.stacktrace.0.machoLoadAddress" is not null + And the error payload field "events.0.exceptions.0.stacktrace.0.machoUUID" is not null + And the error payload field "events.0.exceptions.0.stacktrace.0.machoVMAddress" is not null + And the error payload field "events.0.exceptions.0.stacktrace.0.method" is not null + And the error payload field "events.0.exceptions.0.stacktrace.0.symbolAddress" is not null + And the error payload field "events.0.exceptions.0.stacktrace.0.type" equals "cocoa" + + # the javascript part follows + # On New Arch there is no JS stacktrace - see PLAT-12193 + # We're check the method: asyncGeneratorStep + And the event "exceptions.0.stacktrace.21.columnNumber" equals the version-dependent string: + | arch | version | value | + | new | default | @skip | + | old | default | @not_null | + And the event "exceptions.0.stacktrace.21.file" equals the version-dependent string: + | arch | version | value | + | new | default | @skip | + | old | default | @not_null | + And the event "exceptions.0.stacktrace.21.lineNumber" equals the version-dependent string: + | arch | version | value | + | new | default | @skip | + | old | default | @not_null | + And the event "exceptions.0.stacktrace.21.type" equals the version-dependent string: + | arch | version | value | + | new | default | @skip | + | old | default | @null | + +# Skipped on New Arch below 0.74 - see PLAT-12193 +@skip_new_arch_below_074 +Scenario: Unhandled native promise rejection with native stacktrace + When I run "NativePromiseRejectionUnhandledScenario" + Then I wait to receive an error + And the event "unhandled" is true + And the event "exceptions.0.errorClass" equals "Error" + And the event "exceptions.0.message" equals "NativePromiseRejectionUnhandledScenario" + And the event "exceptions.0.type" equals "reactnativejs" + And the error payload field "events.0.exceptions.0.stacktrace" is a non-empty array + + # the native part of the stack comes first + And the error payload field "events.0.exceptions.0.stacktrace.0.frameAddress" is not null + And the error payload field "events.0.exceptions.0.stacktrace.0.machoFile" equals "reactnative" + And the error payload field "events.0.exceptions.0.stacktrace.0.machoLoadAddress" is not null + And the error payload field "events.0.exceptions.0.stacktrace.0.machoUUID" is not null + And the error payload field "events.0.exceptions.0.stacktrace.0.machoVMAddress" is not null + And the error payload field "events.0.exceptions.0.stacktrace.0.method" is not null + And the error payload field "events.0.exceptions.0.stacktrace.0.symbolAddress" is not null + And the error payload field "events.0.exceptions.0.stacktrace.0.type" equals "cocoa" + + # the javascript part follows + # On New Arch there is no JS stacktrace - see PLAT-12193 + # We're check the method: asyncGeneratorStep + And the event "exceptions.0.stacktrace.21.columnNumber" equals the version-dependent string: + | arch | version | value | + | new | default | @skip | + | old | default | @not_null | + And the event "exceptions.0.stacktrace.21.file" equals the version-dependent string: + | arch | version | value | + | new | default | @skip | + | old | default | @not_null | + And the event "exceptions.0.stacktrace.21.lineNumber" equals the version-dependent string: + | arch | version | value | + | new | default | @skip | + | old | default | @not_null | + And the event "exceptions.0.stacktrace.21.type" equals the version-dependent string: + | arch | version | value | + | new | default | @skip | + | old | default | @null | + +@skip_old_arch @skip_new_arch_below_074 +Scenario: Unhandled synchronous turbo module exception with native stacktrace + When I run "UnhandledNativeErrorSyncScenario" and relaunch the crashed app + And I configure Bugsnag for "UnhandledNativeErrorSyncScenario" + Then I wait to receive an error + And the event "unhandled" is true + + # First exception is the JS Error with JS stacktrace + And the event "exceptions.0.errorClass" equals "Error" + And the event "exceptions.0.message" equals the version-dependent string: + | arch | version | value | + | new | 0.74 | Exception in HostFunction: UnhandledNativeErrorScenario | + | new | default | BugsnagTestInterface.runScenarioSync raised an exception: UnhandledNativeErrorScenario | + + And the event "exceptions.0.type" equals "reactnativejs" + And the event "exceptions.0.stacktrace.0.method" equals "runScenarioSync" + And the event "exceptions.0.stacktrace.0.file" equals "(native)" + And the event "exceptions.0.stacktrace.1.method" equals "run" + And the error payload field "events.0.exceptions.0.stacktrace.1.file" matches the regex "main\.jsbundle$" + + # Second exception (cause) is the native exception with native stacktrace + And the event "exceptions.1.errorClass" equals "NSException" + And the event "exceptions.1.message" equals "UnhandledNativeErrorScenario" + And the event "exceptions.1.type" equals "reactnativejs" + And each element in error payload field "events.0.exceptions.1.stacktrace" has "frameAddress" + And each element in error payload field "events.0.exceptions.1.stacktrace" has "machoFile" + And each element in error payload field "events.0.exceptions.1.stacktrace" has "machoLoadAddress" + And each element in error payload field "events.0.exceptions.1.stacktrace" has "machoUUID" + And each element in error payload field "events.0.exceptions.1.stacktrace" has "machoVMAddress" + And each element in error payload field "events.0.exceptions.1.stacktrace" has "symbolAddress" From 90fb933d71eb31dbbfd753ea059e7f3ecb68960b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 03:45:03 +0000 Subject: [PATCH 26/52] build(deps): bump github/codeql-action from 4.32.2 to 4.32.6 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.32.2 to 4.32.6. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2...0d579ffd059c29b07949a3cce3983f0780820c98) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.32.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 6 +++--- .github/workflows/scorecard.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a5925fd46a..fc822599e6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -47,7 +47,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2 + uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -61,7 +61,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, 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@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2 + uses: github/codeql-action/autobuild@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -74,6 +74,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2 + uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 75895191bf..3ec06622a1 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -68,7 +68,7 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2 + uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 with: sarif_file: results.sarif From 84d9e2be6e4f7706679e6453078cb6d886c80890 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 03:45:11 +0000 Subject: [PATCH 27/52] build(deps): bump ruby/setup-ruby from 1.288.0 to 1.290.0 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.288.0 to 1.290.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Changelog](https://github.com/ruby/setup-ruby/blob/master/release.rb) - [Commits](https://github.com/ruby/setup-ruby/compare/09a7688d3b55cf0e976497ff046b70949eeaccfd...6ca151fd1bfcfd6fe0c4eb6837eb0584d0134a0c) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-version: 1.290.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/aws-lambda.yml | 2 +- .github/workflows/update-dependencies.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/aws-lambda.yml b/.github/workflows/aws-lambda.yml index dc959665ac..30b179f67d 100644 --- a/.github/workflows/aws-lambda.yml +++ b/.github/workflows/aws-lambda.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Ruby - uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1.288.0 + uses: ruby/setup-ruby@6ca151fd1bfcfd6fe0c4eb6837eb0584d0134a0c # v1.290.0 with: ruby-version: '3.1' diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index c0417b79c9..4fac162c74 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -40,7 +40,7 @@ jobs: - run: git submodule update --init --recursive - name: Install ruby - uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1.288.0 + uses: ruby/setup-ruby@6ca151fd1bfcfd6fe0c4eb6837eb0584d0134a0c # v1.290.0 with: ruby-version: 2.7 From 5cc5c51be0cb6fb63f4bfe6e81af18671350f487 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 03:45:17 +0000 Subject: [PATCH 28/52] build(deps): bump actions/setup-node from 6.2.0 to 6.3.0 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.2.0 to 6.3.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/6044e13b5dc448c55e2357c09f80417699197238...53b83947a5a98c8d113130e565377fae1a50d02f) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: 6.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/aws-lambda.yml | 2 +- .github/workflows/pr-diff.yml | 2 +- .github/workflows/test-electron.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/aws-lambda.yml b/.github/workflows/aws-lambda.yml index dc959665ac..8b98b450f8 100644 --- a/.github/workflows/aws-lambda.yml +++ b/.github/workflows/aws-lambda.yml @@ -29,7 +29,7 @@ jobs: ruby-version: '3.1' - name: Install Node - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: 18 diff --git a/.github/workflows/pr-diff.yml b/.github/workflows/pr-diff.yml index d368b3a979..e5747c63d5 100644 --- a/.github/workflows/pr-diff.yml +++ b/.github/workflows/pr-diff.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Setup node - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: 18.x diff --git a/.github/workflows/test-electron.yml b/.github/workflows/test-electron.yml index 35c0abbad6..607ab784ba 100644 --- a/.github/workflows/test-electron.yml +++ b/.github/workflows/test-electron.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: ${{ matrix.node-version }} - name: (Act) install build tools and dependencies From 9e7e113b43cc4d9e25521b254603fa626331ce51 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Tue, 10 Feb 2026 17:24:50 +0000 Subject: [PATCH 29/52] Revert "Remove code coverage from pr diff bot" --- .github/workflows/pr-diff.yml | 4 ++++ dangerfile.js | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr-diff.yml b/.github/workflows/pr-diff.yml index e5747c63d5..aa3fe366fc 100644 --- a/.github/workflows/pr-diff.yml +++ b/.github/workflows/pr-diff.yml @@ -25,8 +25,10 @@ jobs: mkdir .diff npm ci npm run build + npm run test:coverage cat packages/browser/dist/bugsnag.min.js | wc -c > .diff/size-before-minified cat packages/browser/dist/bugsnag.min.js | gzip | wc -c > .diff/size-before-gzipped + cp coverage/coverage-summary.json .diff/coverage-before.json - name: Checkout PR branch uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -39,8 +41,10 @@ jobs: run: | npm ci npm run build + npm run test:coverage cat packages/browser/dist/bugsnag.min.js | wc -c > .diff/size-after-minified cat packages/browser/dist/bugsnag.min.js | gzip | wc -c > .diff/size-after-gzipped + cp coverage/coverage-summary.json .diff/coverage-after.json - name: Run danger uses: danger/danger-js@67ed2c1f42fd2fc198cc3c14b43c8f83351f4fe9 # 13.0.5 diff --git a/dangerfile.js b/dangerfile.js index 5c5ef0ffe6..832d254630 100644 --- a/dangerfile.js +++ b/dangerfile.js @@ -1,15 +1,18 @@ /* global markdown */ const { readFileSync } = require('fs') +const coverageDiff = require('coverage-diff') const before = { minified: parseInt(readFileSync(`${__dirname}/.diff/size-before-minified`, 'utf8').trim()), - gzipped: parseInt(readFileSync(`${__dirname}/.diff/size-before-gzipped`, 'utf8').trim()) + gzipped: parseInt(readFileSync(`${__dirname}/.diff/size-before-gzipped`, 'utf8').trim()), + coverage: JSON.parse(readFileSync(`${__dirname}/.diff/coverage-before.json`, 'utf8')) } const after = { minified: parseInt(readFileSync(`${__dirname}/.diff/size-after-minified`, 'utf8').trim()), - gzipped: parseInt(readFileSync(`${__dirname}/.diff/size-after-gzipped`, 'utf8').trim()) + gzipped: parseInt(readFileSync(`${__dirname}/.diff/size-after-gzipped`, 'utf8').trim()), + coverage: JSON.parse(readFileSync(`${__dirname}/.diff/coverage-after.json`, 'utf8')) } const formatKbs = (n) => `${(n / 1000).toFixed(2)} kB` @@ -33,5 +36,5 @@ markdown(` ### code coverage diff -<_temporarily disabled_> +${coverageDiff.diff(before.coverage, after.coverage).results} `) From 923c93a923764b5743751277282ac293a73dc54a Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 11 Mar 2026 12:01:53 +0000 Subject: [PATCH 30/52] fix: :bug: convert preload script into iife --- packages/plugin-electron-ipc/preload.js | 38 +++++++++++++------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/packages/plugin-electron-ipc/preload.js b/packages/plugin-electron-ipc/preload.js index c62e0f6981..926bca4ff0 100644 --- a/packages/plugin-electron-ipc/preload.js +++ b/packages/plugin-electron-ipc/preload.js @@ -1,26 +1,28 @@ +(function () { // preloads run in devtools panes too, but we don't want to run there -if (document.location.protocol === 'devtools:') return + if (document.location.protocol === 'devtools:') return -const { ipcRenderer, contextBridge } = require('electron') -const BugsnagIpcRenderer = require('./bugsnag-ipc-renderer') -const { CHANNEL_CONFIG } = require('./lib/constants') + const { ipcRenderer, contextBridge } = require('electron') + const BugsnagIpcRenderer = require('./bugsnag-ipc-renderer') + const { CHANNEL_CONFIG } = require('./lib/constants') -// one sync call is required on startup to get the main process config -const config = ipcRenderer.sendSync(CHANNEL_CONFIG) -if (!config) throw new Error('Bugsnag was not started in the main process before browser windows were created') + // one sync call is required on startup to get the main process config + const config = ipcRenderer.sendSync(CHANNEL_CONFIG) + if (!config) throw new Error('Bugsnag was not started in the main process before browser windows were created') -// attach config to the exposed interface -BugsnagIpcRenderer.config = JSON.parse(config) + // attach config to the exposed interface + BugsnagIpcRenderer.config = JSON.parse(config) -// attach process info to the exposed interface -const { isMainFrame, sandboxed, type } = process -BugsnagIpcRenderer.process = { isMainFrame, sandboxed, type } + // attach process info to the exposed interface + const { isMainFrame, sandboxed, type } = process + BugsnagIpcRenderer.process = { isMainFrame, sandboxed, type } -// expose Bugsnag as a global object for the browser -try { + // expose Bugsnag as a global object for the browser + try { // assume contextIsolation=true - contextBridge.exposeInMainWorld('__bugsnag_ipc__', BugsnagIpcRenderer) -} catch (e) {} + contextBridge.exposeInMainWorld('__bugsnag_ipc__', BugsnagIpcRenderer) + } catch (e) {} -// expose for other preload scripts to use, this also covers contextIsolation=false -window.__bugsnag_ipc__ = BugsnagIpcRenderer + // expose for other preload scripts to use, this also covers contextIsolation=false + window.__bugsnag_ipc__ = BugsnagIpcRenderer +})() From 00b16a853ca991b6a845a7305ef9f0b7c9eaaa7c Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 11 Mar 2026 12:44:44 +0000 Subject: [PATCH 31/52] test: :wrench: disable electron sandbox mode --- .github/workflows/pr-diff.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/pr-diff.yml b/.github/workflows/pr-diff.yml index aa3fe366fc..3efa504965 100644 --- a/.github/workflows/pr-diff.yml +++ b/.github/workflows/pr-diff.yml @@ -21,6 +21,7 @@ jobs: - name: Record before stats env: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + ELECTRON_DISABLE_SANDBOX: 1 run: | mkdir .diff npm ci @@ -38,6 +39,7 @@ jobs: - name: Record after stats env: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + ELECTRON_DISABLE_SANDBOX: 1 run: | npm ci npm run build From a6292e8bf4cdcbe7ff64cc2e37edc785a6d6daff Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 11 Mar 2026 14:16:01 +0000 Subject: [PATCH 32/52] test: :white_check_mark: remove memory leak from notifier test --- packages/node/test/notifier.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/node/test/notifier.test.ts b/packages/node/test/notifier.test.ts index 45fb56f48f..e14d08f139 100644 --- a/packages/node/test/notifier.test.ts +++ b/packages/node/test/notifier.test.ts @@ -12,6 +12,15 @@ describe('node notifier', () => { Bugsnag._client = null }) + afterEach(() => { + // Clean up process listeners to prevent MaxListenersExceeded warning + // The unhandledRejection and uncaughtException plugins register listeners + // but don't have destroy methods called automatically + process.removeAllListeners('unhandledRejection') + process.removeAllListeners('uncaughtException') + jest.clearAllMocks() + }) + describe('isStarted()', () => { it('returns false when the notifier has not been initialised', () => { expect(Bugsnag.isStarted()).toBe(false) From 1534c8fb05657fc8d465871e78d372bbd54f0fc4 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 11 Mar 2026 14:17:08 +0000 Subject: [PATCH 33/52] test: :white_check_mark: update module path ignore pattern --- jest.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 358588715a..a54329a23d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -53,7 +53,8 @@ module.exports = { 'plugin-browser-session', 'plugin-network-instrumentation' ], { - testEnvironment: '/jest/FixJSDOMEnvironment.js' + testEnvironment: '/jest/FixJSDOMEnvironment.js', + modulePathIgnorePatterns: ['.verdaccio', 'dist', 'examples', 'fixtures'] }), project('react native', [ 'react-native', From b1b6489a4ec6f0c2e8a440b525adc05e23773244 Mon Sep 17 00:00:00 2001 From: SB-priyankap Date: Mon, 9 Mar 2026 18:58:02 +0530 Subject: [PATCH 34/52] made changes to support for RN-version 0.84 --- .buildkite/basic/react-native-android-full-pipeline.yml | 9 ++++++++- .buildkite/basic/react-native-android-pipeline.yml | 4 ++-- .buildkite/basic/react-native-cli-pipeline.yml | 8 ++++---- .buildkite/basic/react-native-ios-full-pipeline.yml | 9 ++++++++- .buildkite/basic/react-native-ios-pipeline.yml | 4 ++-- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/.buildkite/basic/react-native-android-full-pipeline.yml b/.buildkite/basic/react-native-android-full-pipeline.yml index 7c0c1081fc..8073b0d099 100644 --- a/.buildkite/basic/react-native-android-full-pipeline.yml +++ b/.buildkite/basic/react-native-android-full-pipeline.yml @@ -34,6 +34,8 @@ steps: - "0.78" - "0.80" - "0.81" + - "0.82" + - "0.83" java: - "17" node: @@ -81,6 +83,8 @@ steps: - "0.78" - "0.80" - "0.81" + - "0.82" + - "0.83" reactnavigation: - "true" adjustments: @@ -187,7 +191,8 @@ steps: - "0.78" - "0.80" - "0.81" - + - "0.82" + - "0.83" # current latest version (v7.40.1) of react-native-navigation's autolinking tool doesn't support RN 0.73+, # causing a build failure - see https://github.com/wix/react-native-navigation/issues/7821 # TODO: Investigate and try to re-enable when we add tests for more recent React Native versions @@ -236,6 +241,8 @@ steps: - "0.78" - "0.80" - "0.81" + - "0.82" + - "0.83" reactnavigation: - "true" adjustments: diff --git a/.buildkite/basic/react-native-android-pipeline.yml b/.buildkite/basic/react-native-android-pipeline.yml index 66acad308f..14a40bec09 100644 --- a/.buildkite/basic/react-native-android-pipeline.yml +++ b/.buildkite/basic/react-native-android-pipeline.yml @@ -26,7 +26,7 @@ steps: - exit_status: "*" limit: 1 matrix: - - "0.82" + - "0.84" # # End-to-end tests @@ -69,5 +69,5 @@ steps: concurrency_group: "bitbar" concurrency_method: eager matrix: - - "0.82" + - "0.84" diff --git a/.buildkite/basic/react-native-cli-pipeline.yml b/.buildkite/basic/react-native-cli-pipeline.yml index 68a4ce8630..51e21f79f4 100644 --- a/.buildkite/basic/react-native-cli-pipeline.yml +++ b/.buildkite/basic/react-native-cli-pipeline.yml @@ -25,9 +25,9 @@ steps: - "bundle install" - "bundle exec maze-runner features/build-app-tests/build-android-app.feature" matrix: + - "0.83" - "0.82" - "0.81" - - "0.80" retry: automatic: - exit_status: "*" @@ -54,9 +54,9 @@ steps: - "bundle install" - "bundle exec maze-runner features/build-app-tests/build-ios-app.feature" matrix: + - "0.83" - "0.82" - "0.81" - - "0.80" retry: automatic: - exit_status: "*" @@ -92,9 +92,9 @@ steps: concurrency_group: "browserstack-app" concurrency_method: eager matrix: + - "0.83" - "0.82" - "0.81" - - "0.80" retry: automatic: - exit_status: 103 # Appium session failed @@ -127,9 +127,9 @@ steps: concurrency_group: "browserstack-app" concurrency_method: eager matrix: + - "0.83" - "0.82" - "0.81" - - "0.80" retry: automatic: - exit_status: 103 # Appium session failed diff --git a/.buildkite/basic/react-native-ios-full-pipeline.yml b/.buildkite/basic/react-native-ios-full-pipeline.yml index ee4ca239e0..66e94ad385 100644 --- a/.buildkite/basic/react-native-ios-full-pipeline.yml +++ b/.buildkite/basic/react-native-ios-full-pipeline.yml @@ -35,6 +35,8 @@ steps: - "0.78" - "0.80" - "0.81" + - "0.82" + - "0.83" node: - "22" adjustments: @@ -74,6 +76,8 @@ steps: - "0.78" - "0.80" - "0.81" + - "0.82" + - "0.83" reactnavigation: - "true" adjustments: @@ -184,7 +188,8 @@ steps: - "0.78" - "0.80" - "0.81" - + - "0.82" + - "0.83" - label: ":bitbar: :mac: RN {{matrix.reactnative}} iOS (New Arch) end-to-end tests" depends_on: "build-react-native-ios-fixture-new-arch-full" timeout_in_minutes: 60 @@ -229,6 +234,8 @@ steps: - "0.78" - "0.80" - "0.81" + - "0.82" + - "0.83" reactnavigation: - "true" adjustments: diff --git a/.buildkite/basic/react-native-ios-pipeline.yml b/.buildkite/basic/react-native-ios-pipeline.yml index 612967d18c..5522511efd 100644 --- a/.buildkite/basic/react-native-ios-pipeline.yml +++ b/.buildkite/basic/react-native-ios-pipeline.yml @@ -26,7 +26,7 @@ steps: - "bundle install" - "node scripts/generate-react-native-fixture.js" matrix: - - "0.82" + - "0.84" retry: automatic: - exit_status: "*" @@ -71,5 +71,5 @@ steps: concurrency_group: "bitbar" concurrency_method: eager matrix: - - "0.82" + - "0.84" From 416c7c72b60235934ed9ad9e9ac99bbae91ff6f4 Mon Sep 17 00:00:00 2001 From: SB-priyankap Date: Thu, 12 Mar 2026 16:26:10 +0530 Subject: [PATCH 35/52] 15453/fixes for RN version update --- .../scenario-launcher/scenarios/core/NetworkRequestScenario.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkRequestScenario.js b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkRequestScenario.js index 1470610ced..2411f8ab0a 100644 --- a/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkRequestScenario.js +++ b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/NetworkRequestScenario.js @@ -8,7 +8,7 @@ export class NetworkRequestScenario extends Scenario { this.reflectEndpoint = nativeConfig.endpoints.notify.replace('/notify', '/reflect') this.statusCode = scenarioData - const plugin = new BugsnagPluginNetworkInstrumentation({ + const plugin = BugsnagPluginNetworkInstrumentation({ maxRequestSize: 1024, maxResponseSize: 1024 }) From 7201f477a42fb02f504ed7ed2a048d6566e8b287 Mon Sep 17 00:00:00 2001 From: SB-priyankap Date: Thu, 12 Mar 2026 18:12:19 +0530 Subject: [PATCH 36/52] 15453/fixes for RN version update mazerunner test case update for native stackios --- test/react-native/features/native-stack-ios.feature | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/react-native/features/native-stack-ios.feature b/test/react-native/features/native-stack-ios.feature index 0f9228007c..186e640f02 100644 --- a/test/react-native/features/native-stack-ios.feature +++ b/test/react-native/features/native-stack-ios.feature @@ -15,7 +15,9 @@ Scenario: Handled native promise rejection with native stacktrace # the native part of the stack comes first And the error payload field "events.0.exceptions.0.stacktrace.0.frameAddress" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.machoFile" equals "reactnative" + And the error payload field "events.0.exceptions.0.stacktrace.0.machoFile" equals one of: + | reactnative | + | React | And the error payload field "events.0.exceptions.0.stacktrace.0.machoLoadAddress" is not null And the error payload field "events.0.exceptions.0.stacktrace.0.machoUUID" is not null And the error payload field "events.0.exceptions.0.stacktrace.0.machoVMAddress" is not null @@ -56,7 +58,9 @@ Scenario: Unhandled native promise rejection with native stacktrace # the native part of the stack comes first And the error payload field "events.0.exceptions.0.stacktrace.0.frameAddress" is not null - And the error payload field "events.0.exceptions.0.stacktrace.0.machoFile" equals "reactnative" + And the error payload field "events.0.exceptions.0.stacktrace.0.machoFile" equals one of: + | reactnative | + | React | And the error payload field "events.0.exceptions.0.stacktrace.0.machoLoadAddress" is not null And the error payload field "events.0.exceptions.0.stacktrace.0.machoUUID" is not null And the error payload field "events.0.exceptions.0.stacktrace.0.machoVMAddress" is not null From 8ee2216a3909f7f8f09e188fbeec33e8f689191f Mon Sep 17 00:00:00 2001 From: SB-priyankap Date: Tue, 10 Mar 2026 16:53:51 +0530 Subject: [PATCH 37/52] 15453/made changes to support for RN-version 0.84 --- .buildkite/basic/react-native-android-full-pipeline.yml | 6 ------ .buildkite/basic/react-native-cli-pipeline.yml | 8 ++++---- .buildkite/basic/react-native-ios-full-pipeline.yml | 8 ++------ 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/.buildkite/basic/react-native-android-full-pipeline.yml b/.buildkite/basic/react-native-android-full-pipeline.yml index 8073b0d099..39d8b76168 100644 --- a/.buildkite/basic/react-native-android-full-pipeline.yml +++ b/.buildkite/basic/react-native-android-full-pipeline.yml @@ -34,8 +34,6 @@ steps: - "0.78" - "0.80" - "0.81" - - "0.82" - - "0.83" java: - "17" node: @@ -82,7 +80,6 @@ steps: - "0.76" - "0.78" - "0.80" - - "0.81" - "0.82" - "0.83" reactnavigation: @@ -191,8 +188,6 @@ steps: - "0.78" - "0.80" - "0.81" - - "0.82" - - "0.83" # current latest version (v7.40.1) of react-native-navigation's autolinking tool doesn't support RN 0.73+, # causing a build failure - see https://github.com/wix/react-native-navigation/issues/7821 # TODO: Investigate and try to re-enable when we add tests for more recent React Native versions @@ -240,7 +235,6 @@ steps: - "0.76" - "0.78" - "0.80" - - "0.81" - "0.82" - "0.83" reactnavigation: diff --git a/.buildkite/basic/react-native-cli-pipeline.yml b/.buildkite/basic/react-native-cli-pipeline.yml index 51e21f79f4..7b150a5cdb 100644 --- a/.buildkite/basic/react-native-cli-pipeline.yml +++ b/.buildkite/basic/react-native-cli-pipeline.yml @@ -25,9 +25,9 @@ steps: - "bundle install" - "bundle exec maze-runner features/build-app-tests/build-android-app.feature" matrix: + - "0.84" - "0.83" - "0.82" - - "0.81" retry: automatic: - exit_status: "*" @@ -54,9 +54,9 @@ steps: - "bundle install" - "bundle exec maze-runner features/build-app-tests/build-ios-app.feature" matrix: + - "0.84" - "0.83" - "0.82" - - "0.81" retry: automatic: - exit_status: "*" @@ -92,9 +92,9 @@ steps: concurrency_group: "browserstack-app" concurrency_method: eager matrix: + - "0.84" - "0.83" - "0.82" - - "0.81" retry: automatic: - exit_status: 103 # Appium session failed @@ -127,9 +127,9 @@ steps: concurrency_group: "browserstack-app" concurrency_method: eager matrix: + - "0.84" - "0.83" - "0.82" - - "0.81" retry: automatic: - exit_status: 103 # Appium session failed diff --git a/.buildkite/basic/react-native-ios-full-pipeline.yml b/.buildkite/basic/react-native-ios-full-pipeline.yml index 66e94ad385..564f5d63b9 100644 --- a/.buildkite/basic/react-native-ios-full-pipeline.yml +++ b/.buildkite/basic/react-native-ios-full-pipeline.yml @@ -35,8 +35,7 @@ steps: - "0.78" - "0.80" - "0.81" - - "0.82" - - "0.83" + node: - "22" adjustments: @@ -75,7 +74,6 @@ steps: - "0.76" - "0.78" - "0.80" - - "0.81" - "0.82" - "0.83" reactnavigation: @@ -188,8 +186,7 @@ steps: - "0.78" - "0.80" - "0.81" - - "0.82" - - "0.83" + - label: ":bitbar: :mac: RN {{matrix.reactnative}} iOS (New Arch) end-to-end tests" depends_on: "build-react-native-ios-fixture-new-arch-full" timeout_in_minutes: 60 @@ -233,7 +230,6 @@ steps: - "0.76" - "0.78" - "0.80" - - "0.81" - "0.82" - "0.83" reactnavigation: From c8127f203889e5d531de0c567e653c0c2765753b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 03:44:57 +0000 Subject: [PATCH 38/52] build(deps): bump actions/cache from 5.0.3 to 5.0.4 Bumps [actions/cache](https://github.com/actions/cache) from 5.0.3 to 5.0.4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/cdf6c1fa76f9f475f3d7449005a359c84ca0f306...668228422ae6a00e4ad889ee87cd7109ec5666a7) --- updated-dependencies: - dependency-name: actions/cache dependency-version: 5.0.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/test-electron.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-electron.yml b/.github/workflows/test-electron.yml index 607ab784ba..623ef81ea2 100644 --- a/.github/workflows/test-electron.yml +++ b/.github/workflows/test-electron.yml @@ -30,7 +30,7 @@ jobs: if: ${{ !env.ACT }} run: | echo "::set-output name=dir::$(npm config get cache)" - - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: npm-cache if: ${{ !env.ACT }} with: From 509975040d83d7f89b9c4fd305ccce2512017ff3 Mon Sep 17 00:00:00 2001 From: SB-priyankap Date: Wed, 25 Mar 2026 13:32:51 +0530 Subject: [PATCH 39/52] Added test cases for onError and addOnError callback * 7765-added test cases for onError and addOnError callback * 7765 added unit test case for addOnError callback * plat-7765 remove spaces --- packages/react-native/test/index.test.ts | 16 ++++++++++++++++ .../scenarios/core/AddOnErrorCallbackScenario.js | 13 +++++++++++++ .../scenarios/core/OnErrorCallbackScenario.js | 10 ++++++++++ .../scenario-launcher/scenarios/core/index.js | 4 ++++ .../features/onerror_callback.feature | 11 +++++++++++ 5 files changed, 54 insertions(+) create mode 100644 test/react-native/features/fixtures/scenario-launcher/scenarios/core/AddOnErrorCallbackScenario.js create mode 100644 test/react-native/features/fixtures/scenario-launcher/scenarios/core/OnErrorCallbackScenario.js create mode 100644 test/react-native/features/onerror_callback.feature diff --git a/packages/react-native/test/index.test.ts b/packages/react-native/test/index.test.ts index 857b848c08..cbef6133b3 100644 --- a/packages/react-native/test/index.test.ts +++ b/packages/react-native/test/index.test.ts @@ -125,4 +125,20 @@ describe('react native notifier', () => { expect(Bugsnag.isStarted()).toBe(true) }) }) + + it('calls addOnError callback for all errors', (done) => { + Bugsnag.start({}) + const addOnErrorCb = jest.fn((event) => { + event.addMetadata('addonError', { called: true }) + return true + }) + Bugsnag.addOnError(addOnErrorCb) + const error = new Error('addonError test') + Bugsnag.notify(error, undefined, (err, event) => { + if (err) return done(err) + expect(addOnErrorCb).toHaveBeenCalledWith(expect.any(Object)) + expect(event.getMetadata('addonError')).toEqual({ called: true }) + done() + }) + }) }) diff --git a/test/react-native/features/fixtures/scenario-launcher/scenarios/core/AddOnErrorCallbackScenario.js b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/AddOnErrorCallbackScenario.js new file mode 100644 index 0000000000..5710d5e26a --- /dev/null +++ b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/AddOnErrorCallbackScenario.js @@ -0,0 +1,13 @@ + +import Scenario from './Scenario' +import Bugsnag from '@bugsnag/react-native' + +export class AddOnErrorCallbackScenario extends Scenario { + run () { + Bugsnag.addOnError(event => { + event.addMetadata('addonError', { scenario: true }) + return true + }) + Bugsnag.notify(new Error('addonError scenario test')) + } +} diff --git a/test/react-native/features/fixtures/scenario-launcher/scenarios/core/OnErrorCallbackScenario.js b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/OnErrorCallbackScenario.js new file mode 100644 index 0000000000..db60113d05 --- /dev/null +++ b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/OnErrorCallbackScenario.js @@ -0,0 +1,10 @@ +import Scenario from './Scenario' +import Bugsnag from '@bugsnag/react-native' + +export class OnErrorCallbackScenario extends Scenario { + run () { + Bugsnag.notify(new Error('addonError scenario test'),event=>{ + event.addMetadata('onError', { scenario: true }) + }) + } +} diff --git a/test/react-native/features/fixtures/scenario-launcher/scenarios/core/index.js b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/index.js index 1075c4c8c6..31aedacfad 100644 --- a/test/react-native/features/fixtures/scenario-launcher/scenarios/core/index.js +++ b/test/react-native/features/fixtures/scenario-launcher/scenarios/core/index.js @@ -84,3 +84,7 @@ export { GroupingDiscriminatorNativeScenario } from './GroupingDiscriminatorNati // http_errors.feature export { NetworkRequestScenario } from './NetworkRequestScenario' + +export { AddOnErrorCallbackScenario } from './AddOnErrorCallbackScenario' + +export { OnErrorCallbackScenario } from './OnErrorCallbackScenario' \ No newline at end of file diff --git a/test/react-native/features/onerror_callback.feature b/test/react-native/features/onerror_callback.feature new file mode 100644 index 0000000000..f0d02698cc --- /dev/null +++ b/test/react-native/features/onerror_callback.feature @@ -0,0 +1,11 @@ +Feature: React Native addOnError & onError callbacks + +Scenario: Event is modified by addOnError callback + When I run "AddOnErrorCallbackScenario" + Then I wait to receive an error + And the event "metaData.addonError.scenario" is true + + Scenario: Event is modified by onError callback + When I run "OnErrorCallbackScenario" + Then I wait to receive an error + And the event "metaData.onError.scenario" is true \ No newline at end of file From 4c8e2699d391ba965d8559ca2c987bfaf37c8c05 Mon Sep 17 00:00:00 2001 From: SB-priyankap Date: Wed, 25 Mar 2026 13:36:21 +0530 Subject: [PATCH 40/52] added extra parameter inside inline script update added extra parameter inside inline script update --- CHANGELOG.md | 1 + .../plugin-inline-script-content/inline-script-content.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f90a74287..e40f59271a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Changed +- (plugin-inline-script-content) Add support for additional event target constructors including MediaSource, MediaRecorder, ServiceWorker, RTCPeerConnection, and others [#2700](https://github.com/bugsnag/bugsnag-js/pull/2700) - (plugin-network-instrumentation) Manually parse URLs to improve React Native compatibility [#2674](https://github.com/bugsnag/bugsnag-js/pull/2674) ### Fixed diff --git a/packages/plugin-inline-script-content/inline-script-content.js b/packages/plugin-inline-script-content/inline-script-content.js index 26749c6a2f..f654229738 100644 --- a/packages/plugin-inline-script-content/inline-script-content.js +++ b/packages/plugin-inline-script-content/inline-script-content.js @@ -111,7 +111,10 @@ module.exports = (doc = document, win = window) => ({ 'CryptoOperation', 'EventSource', 'FileReader', 'HTMLUnknownElement', 'IDBDatabase', 'IDBRequest', 'IDBTransaction', 'KeyOperation', 'MediaController', 'MessagePort', 'ModalWindow', 'Notification', 'SVGElementInstance', 'Screen', 'TextTrack', 'TextTrackCue', 'TextTrackList', - 'WebSocket', 'WebSocketWorker', 'Worker', 'XMLHttpRequest', 'XMLHttpRequestEventTarget', 'XMLHttpRequestUpload' + 'WebSocket', 'WebSocketWorker', 'Worker', 'XMLHttpRequest', 'XMLHttpRequestEventTarget', 'XMLHttpRequestUpload', + 'MediaSource', 'MediaRecorder', 'MediaStream', 'ServiceWorker', 'ServiceWorkerContainer', + 'ServiceWorkerRegistration', 'BroadcastChannel', 'RTCPeerConnection', 'RTCDataChannel', 'AbortSignal', + 'MediaQueryList', 'ShadowRoot', 'FontFaceSet', 'Animation', 'PermissionStatus', 'PaymentRequest', 'VideoTrackList' ], o => { if (!win[o] || !win[o].prototype || !Object.prototype.hasOwnProperty.call(win[o].prototype, 'addEventListener')) return __proxy(win[o].prototype, 'addEventListener', original => From 75f90d087870de531ea4432a9f8826cf4c8a1354 Mon Sep 17 00:00:00 2001 From: Bumpsnag bot <> Date: Thu, 26 Mar 2026 17:08:06 +0000 Subject: [PATCH 41/52] Update bugsnag-cocoa to v6.36.0 [full ci] --- CHANGELOG.md | 4 ++-- packages/react-native/ios/vendor/bugsnag-cocoa | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e40f59271a..e198a22b2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,13 +12,14 @@ - (plugin-network-instrumentation) Report HTTP Errors as handled [#2662](https://github.com/bugsnag/bugsnag-js/pull/2662) - (plugin-network-instrumentation) Omit stacktraces from HTTP Error events [#2684](https://github.com/bugsnag/bugsnag-js/pull/2684) - (react-native) Report error cause with native stacktrace for Turbo Module exceptions [#2686] (https://github.com/bugsnag/bugsnag-js/pull/2686) - + ### Dependencies - Update bugsnag-cocoa to [v6.35.0](https//github.com/bugsnag/bugsnag-cocoa/releases/tag/v6.35.0) [#2663](https://github.com/bugsnag/bugsnag-js/pull/2663) - Update bugsnag-android to [v6.22.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.22.0) [#2656](https://github.com/bugsnag/bugsnag-js/pull/2656) - Update bugsnag-android to [v6.23.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.23.0) [#2673](https://github.com/bugsnag/bugsnag-js/pull/2673) - Update bugsnag-android to [v6.25.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.25.0) [#2689](https://github.com/bugsnag/bugsnag-js/pull/2689) +Update bugsnag-cocoa to [v6.36.0](https//github.com/bugsnag/bugsnag-cocoa/releases/tag/v6.36.0) [#2707](https://github.com/bugsnag/bugsnag-js/pull/2707) ## [8.8.1] - 2026-01-26 @@ -29,7 +30,6 @@ ## [8.8.0] - 2026-01-20 This release adds support for notitfying failed network requests using the new `plugin-network-instrumentation` package - ### Added - (plugin-network-instrumentation) Add new plugin to notify failed network requests [#2647](https://github.com/bugsnag/bugsnag-js/pull/2647) diff --git a/packages/react-native/ios/vendor/bugsnag-cocoa b/packages/react-native/ios/vendor/bugsnag-cocoa index ae1094f4df..7f57276b28 160000 --- a/packages/react-native/ios/vendor/bugsnag-cocoa +++ b/packages/react-native/ios/vendor/bugsnag-cocoa @@ -1 +1 @@ -Subproject commit ae1094f4dfd3f946f00f27e5e6771fd36243a342 +Subproject commit 7f57276b282dde418e602a8cc02a6a1e22993aae From 80990e1b11a3d5cbb9753165bf9e78a369ca25cc Mon Sep 17 00:00:00 2001 From: SB-priyankap Date: Fri, 27 Mar 2026 12:16:18 +0530 Subject: [PATCH 42/52] Bump CUID version and replace validation * plat-12013 update cuid version and replace validation * plat-12013 push package.lock changes * plat-12013 fix eslint issue and update changelog.md file --- CHANGELOG.md | 1 + package-lock.json | 63 +++++++++++++++------ packages/core/package.json | 2 +- packages/in-flight/package.json | 2 +- packages/plugin-browser-device/device.js | 7 +-- packages/plugin-browser-device/package.json | 2 +- 6 files changed, 54 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e40f59271a..f2995f7f04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ### Dependencies +- Update @bugsnag/cuid to [v3.2.1] (https://github.com/bugsnag/cuid/releases/tag/v3.2.1) [#2706](https://github.com/bugsnag/bugsnag-js/pull/2706) - Update bugsnag-cocoa to [v6.35.0](https//github.com/bugsnag/bugsnag-cocoa/releases/tag/v6.35.0) [#2663](https://github.com/bugsnag/bugsnag-js/pull/2663) - Update bugsnag-android to [v6.22.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.22.0) [#2656](https://github.com/bugsnag/bugsnag-js/pull/2656) - Update bugsnag-android to [v6.23.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.23.0) [#2673](https://github.com/bugsnag/bugsnag-js/pull/2673) diff --git a/package-lock.json b/package-lock.json index b75e7d4e1a..9c910e8918 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2627,11 +2627,6 @@ "resolved": "packages/core", "link": true }, - "node_modules/@bugsnag/cuid": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@bugsnag/cuid/-/cuid-3.1.1.tgz", - "integrity": "sha512-d2z4b0rEo3chI07FNN1Xds8v25CNeekecU6FC/2Fs9MxY2EipkZTThVcV2YinMn8dvRUlViKOyC50evoUxg8tw==" - }, "node_modules/@bugsnag/delivery-electron": { "resolved": "packages/delivery-electron", "link": true @@ -46675,13 +46670,19 @@ "version": "8.8.0", "license": "MIT", "dependencies": { - "@bugsnag/cuid": "^3.0.0", + "@bugsnag/cuid": "^3.2.1", "@bugsnag/safe-json-stringify": "^6.0.0", "error-stack-parser": "^2.0.3", "iserror": "^0.0.2", "stack-generator": "^2.0.3" } }, + "packages/core/node_modules/@bugsnag/cuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@bugsnag/cuid/-/cuid-3.2.1.tgz", + "integrity": "sha512-zpvN8xQ5rdRWakMd/BcVkdn2F8HKlDSbM3l7duueK590WmI1T0ObTLc1V/1e55r14WNjPd5AJTYX4yPEAFVi+Q==", + "license": "MIT" + }, "packages/delivery-electron": { "name": "@bugsnag/delivery-electron", "version": "8.8.0", @@ -46818,7 +46819,7 @@ "version": "8.8.0", "license": "MIT", "dependencies": { - "@bugsnag/cuid": "^3.0.0" + "@bugsnag/cuid": "^3.2.1" }, "devDependencies": { "@bugsnag/core": "^8.8.0" @@ -46827,6 +46828,12 @@ "@bugsnag/core": "^8.0.0" } }, + "packages/in-flight/node_modules/@bugsnag/cuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@bugsnag/cuid/-/cuid-3.2.1.tgz", + "integrity": "sha512-zpvN8xQ5rdRWakMd/BcVkdn2F8HKlDSbM3l7duueK590WmI1T0ObTLc1V/1e55r14WNjPd5AJTYX4yPEAFVi+Q==", + "license": "MIT" + }, "packages/js": { "name": "@bugsnag/js", "version": "8.8.1", @@ -52954,7 +52961,7 @@ "version": "8.8.0", "license": "MIT", "dependencies": { - "@bugsnag/cuid": "^3.0.0" + "@bugsnag/cuid": "^3.2.1" }, "devDependencies": { "@bugsnag/core": "^8.8.0" @@ -52963,6 +52970,12 @@ "@bugsnag/core": "^8.0.0" } }, + "packages/plugin-browser-device/node_modules/@bugsnag/cuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@bugsnag/cuid/-/cuid-3.2.1.tgz", + "integrity": "sha512-zpvN8xQ5rdRWakMd/BcVkdn2F8HKlDSbM3l7duueK590WmI1T0ObTLc1V/1e55r14WNjPd5AJTYX4yPEAFVi+Q==", + "license": "MIT" + }, "packages/plugin-browser-request": { "name": "@bugsnag/plugin-browser-request", "version": "8.8.0", @@ -53037,6 +53050,7 @@ "packages/plugin-electron-app": { "name": "@bugsnag/plugin-electron-app", "version": "8.8.0", + "hasInstallScript": true, "license": "MIT", "dependencies": { "bindings": "^1.5.0" @@ -53078,6 +53092,7 @@ "packages/plugin-electron-client-state-persistence": { "name": "@bugsnag/plugin-electron-client-state-persistence", "version": "8.8.0", + "hasInstallScript": true, "license": "MIT", "dependencies": { "bindings": "^1.5.0" @@ -55675,18 +55690,20 @@ "@bugsnag/core": { "version": "file:packages/core", "requires": { - "@bugsnag/cuid": "^3.0.0", + "@bugsnag/cuid": "^3.2.1", "@bugsnag/safe-json-stringify": "^6.0.0", "error-stack-parser": "^2.0.3", "iserror": "^0.0.2", "stack-generator": "^2.0.3" + }, + "dependencies": { + "@bugsnag/cuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@bugsnag/cuid/-/cuid-3.2.1.tgz", + "integrity": "sha512-zpvN8xQ5rdRWakMd/BcVkdn2F8HKlDSbM3l7duueK590WmI1T0ObTLc1V/1e55r14WNjPd5AJTYX4yPEAFVi+Q==" + } } }, - "@bugsnag/cuid": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@bugsnag/cuid/-/cuid-3.1.1.tgz", - "integrity": "sha512-d2z4b0rEo3chI07FNN1Xds8v25CNeekecU6FC/2Fs9MxY2EipkZTThVcV2YinMn8dvRUlViKOyC50evoUxg8tw==" - }, "@bugsnag/delivery-electron": { "version": "file:packages/delivery-electron", "requires": { @@ -55780,7 +55797,14 @@ "version": "file:packages/in-flight", "requires": { "@bugsnag/core": "^8.8.0", - "@bugsnag/cuid": "^3.0.0" + "@bugsnag/cuid": "^3.2.1" + }, + "dependencies": { + "@bugsnag/cuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@bugsnag/cuid/-/cuid-3.2.1.tgz", + "integrity": "sha512-zpvN8xQ5rdRWakMd/BcVkdn2F8HKlDSbM3l7duueK590WmI1T0ObTLc1V/1e55r14WNjPd5AJTYX4yPEAFVi+Q==" + } } }, "@bugsnag/js": { @@ -59758,7 +59782,14 @@ "version": "file:packages/plugin-browser-device", "requires": { "@bugsnag/core": "^8.8.0", - "@bugsnag/cuid": "^3.0.0" + "@bugsnag/cuid": "^3.2.1" + }, + "dependencies": { + "@bugsnag/cuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@bugsnag/cuid/-/cuid-3.2.1.tgz", + "integrity": "sha512-zpvN8xQ5rdRWakMd/BcVkdn2F8HKlDSbM3l7duueK590WmI1T0ObTLc1V/1e55r14WNjPd5AJTYX4yPEAFVi+Q==" + } } }, "@bugsnag/plugin-browser-request": { diff --git a/packages/core/package.json b/packages/core/package.json index 2743f6d6a6..00f3e176be 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -20,7 +20,7 @@ "author": "Bugsnag", "license": "MIT", "dependencies": { - "@bugsnag/cuid": "^3.0.0", + "@bugsnag/cuid": "^3.2.1", "@bugsnag/safe-json-stringify": "^6.0.0", "error-stack-parser": "^2.0.3", "iserror": "^0.0.2", diff --git a/packages/in-flight/package.json b/packages/in-flight/package.json index 0153c00ffa..ce406b7ad6 100644 --- a/packages/in-flight/package.json +++ b/packages/in-flight/package.json @@ -19,7 +19,7 @@ "author": "Bugsnag", "license": "MIT", "dependencies": { - "@bugsnag/cuid": "^3.0.0" + "@bugsnag/cuid": "^3.2.1" }, "devDependencies": { "@bugsnag/core": "^8.8.0" diff --git a/packages/plugin-browser-device/device.js b/packages/plugin-browser-device/device.js index bd1437b970..d1d60f75d8 100644 --- a/packages/plugin-browser-device/device.js +++ b/packages/plugin-browser-device/device.js @@ -1,5 +1,6 @@ const assign = require('@bugsnag/core/lib/es-utils/assign') const BUGSNAG_ANONYMOUS_ID_KEY = 'bugsnag-anonymous-id' +const cuid = require('@bugsnag/cuid') const getDeviceId = (win) => { try { @@ -7,13 +8,11 @@ const getDeviceId = (win) => { let id = storage.getItem(BUGSNAG_ANONYMOUS_ID_KEY) - // If we get an ID, make sure it looks like a valid cuid. The length can - // fluctuate slightly, so some leeway is built in - if (id && /^c[a-z0-9]{20,32}$/.test(id)) { + // If we get an ID, make sure it looks like a valid cuid + if (id && cuid.isCuid(id)) { return id } - const cuid = require('@bugsnag/cuid') id = cuid() storage.setItem(BUGSNAG_ANONYMOUS_ID_KEY, id) diff --git a/packages/plugin-browser-device/package.json b/packages/plugin-browser-device/package.json index f2711a1b7b..fcf8019eff 100644 --- a/packages/plugin-browser-device/package.json +++ b/packages/plugin-browser-device/package.json @@ -17,7 +17,7 @@ "author": "Bugsnag", "license": "MIT", "dependencies": { - "@bugsnag/cuid": "^3.0.0" + "@bugsnag/cuid": "^3.2.1" }, "devDependencies": { "@bugsnag/core": "^8.8.0" From b919927803684ef9212eccdddccf1b1c83f631da Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Fri, 27 Mar 2026 09:26:44 +0000 Subject: [PATCH 43/52] Apply suggestion from @gingerbenw --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e198a22b2a..98e0aa8e09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ - Update bugsnag-android to [v6.22.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.22.0) [#2656](https://github.com/bugsnag/bugsnag-js/pull/2656) - Update bugsnag-android to [v6.23.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.23.0) [#2673](https://github.com/bugsnag/bugsnag-js/pull/2673) - Update bugsnag-android to [v6.25.0](https//github.com/bugsnag/bugsnag-android/releases/tag/v6.25.0) [#2689](https://github.com/bugsnag/bugsnag-js/pull/2689) -Update bugsnag-cocoa to [v6.36.0](https//github.com/bugsnag/bugsnag-cocoa/releases/tag/v6.36.0) [#2707](https://github.com/bugsnag/bugsnag-js/pull/2707) +- Update bugsnag-cocoa to [v6.36.0](https//github.com/bugsnag/bugsnag-cocoa/releases/tag/v6.36.0) [#2707](https://github.com/bugsnag/bugsnag-js/pull/2707) ## [8.8.1] - 2026-01-26 From 0a79eb65efc7abeecd3cccfd8a3aaa454a8a6eda Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 03:45:16 +0000 Subject: [PATCH 44/52] build(deps): bump github/codeql-action from 4.32.6 to 4.35.1 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.32.6 to 4.35.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/0d579ffd059c29b07949a3cce3983f0780820c98...c10b8064de6f491fea524254123dbe5e09572f13) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.35.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 6 +++--- .github/workflows/scorecard.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index fc822599e6..003df38bfb 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -47,7 +47,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 + uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -61,7 +61,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, 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@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 + uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -74,6 +74,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 + uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 3ec06622a1..552a58c94a 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -68,7 +68,7 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6 + uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: sarif_file: results.sarif From c6b3578eff0ddb6c5accb681a94aab26f69bff3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 03:45:25 +0000 Subject: [PATCH 45/52] build(deps): bump ruby/setup-ruby from 1.290.0 to 1.299.0 Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.290.0 to 1.299.0. - [Release notes](https://github.com/ruby/setup-ruby/releases) - [Changelog](https://github.com/ruby/setup-ruby/blob/master/release.rb) - [Commits](https://github.com/ruby/setup-ruby/compare/6ca151fd1bfcfd6fe0c4eb6837eb0584d0134a0c...3ff19f5e2baf30647122352b96108b1fbe250c64) --- updated-dependencies: - dependency-name: ruby/setup-ruby dependency-version: 1.299.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/aws-lambda.yml | 2 +- .github/workflows/update-dependencies.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/aws-lambda.yml b/.github/workflows/aws-lambda.yml index 4dee97d5f9..462d465662 100644 --- a/.github/workflows/aws-lambda.yml +++ b/.github/workflows/aws-lambda.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Ruby - uses: ruby/setup-ruby@6ca151fd1bfcfd6fe0c4eb6837eb0584d0134a0c # v1.290.0 + uses: ruby/setup-ruby@3ff19f5e2baf30647122352b96108b1fbe250c64 # v1.299.0 with: ruby-version: '3.1' diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index 4fac162c74..673052eae6 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -40,7 +40,7 @@ jobs: - run: git submodule update --init --recursive - name: Install ruby - uses: ruby/setup-ruby@6ca151fd1bfcfd6fe0c4eb6837eb0584d0134a0c # v1.290.0 + uses: ruby/setup-ruby@3ff19f5e2baf30647122352b96108b1fbe250c64 # v1.299.0 with: ruby-version: 2.7 From f273ea122386cc25b0552f4d84aef14cbbd29f2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 03:45:28 +0000 Subject: [PATCH 46/52] build(deps): bump gradle/actions from 5.0.2 to 6.0.1 Bumps [gradle/actions](https://github.com/gradle/actions) from 5.0.2 to 6.0.1. - [Release notes](https://github.com/gradle/actions/releases) - [Commits](https://github.com/gradle/actions/compare/0723195856401067f7a2779048b490ace7a47d7c...39e147cb9de83bb9910b8ef8bd7fff0ee20fcd6f) --- updated-dependencies: - dependency-name: gradle/actions dependency-version: 6.0.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 3ec06622a1..25853b54fb 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -77,4 +77,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: gradle/actions/wrapper-validation@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2 + - uses: gradle/actions/wrapper-validation@39e147cb9de83bb9910b8ef8bd7fff0ee20fcd6f # v6.0.1 From 3caa7daf7b5718080da22adf51c853340e1ca464 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Mon, 30 Mar 2026 10:32:12 +0100 Subject: [PATCH 47/52] Merge pull request #2667 from bugsnag/react-native-delivery-request-response Handle request and response objects in @bugsnag/delivery-react-native package --- .buildkite/package_manifest.json | 3 + CHANGELOG.md | 5 + packages/delivery-react-native/delivery.js | 2 + .../test/delivery.test.ts | 6 + .../lib/extract-domain.js | 34 ----- .../lib/parse-query-params.js | 29 ----- .../lib/parse-query-string.js | 19 +++ .../lib/parse-url.js | 64 ++++++++++ .../lib/redact-query-parameters.js | 23 ---- .../network-instrumentation.js | 32 +++-- .../test/network-instrumentation-xhr.test.ts | 27 ---- .../test/network-instrumentation.test.ts | 43 ++++++- .../test/parse-query-string.test.ts | 85 +++++++++++++ .../test/parse-url.test.ts | 117 ++++++++++++++++++ .../test/redact-query-parameters.test.ts | 24 ---- .../BugsnagEventDeserializer.m | 16 +++ test/browser/features/http_errors.feature | 16 +-- .../{401.json => 401/exceptions.json} | 2 +- .../expected_http_errors/401/request.json | 8 ++ .../expected_http_errors/401/response.json | 6 + .../{500.json => 500/exceptions.json} | 2 +- .../expected_http_errors/500/request.json | 8 ++ .../expected_http_errors/500/response.json | 6 + .../react-native/features/http_errors.feature | 4 +- 24 files changed, 413 insertions(+), 168 deletions(-) delete mode 100644 packages/plugin-network-instrumentation/lib/extract-domain.js delete mode 100644 packages/plugin-network-instrumentation/lib/parse-query-params.js create mode 100644 packages/plugin-network-instrumentation/lib/parse-query-string.js create mode 100644 packages/plugin-network-instrumentation/lib/parse-url.js delete mode 100644 packages/plugin-network-instrumentation/lib/redact-query-parameters.js create mode 100644 packages/plugin-network-instrumentation/test/parse-query-string.test.ts create mode 100644 packages/plugin-network-instrumentation/test/parse-url.test.ts delete mode 100644 packages/plugin-network-instrumentation/test/redact-query-parameters.test.ts rename test/react-native/features/fixtures/expected_http_errors/{401.json => 401/exceptions.json} (66%) create mode 100644 test/react-native/features/fixtures/expected_http_errors/401/request.json create mode 100644 test/react-native/features/fixtures/expected_http_errors/401/response.json rename test/react-native/features/fixtures/expected_http_errors/{500.json => 500/exceptions.json} (66%) create mode 100644 test/react-native/features/fixtures/expected_http_errors/500/request.json create mode 100644 test/react-native/features/fixtures/expected_http_errors/500/response.json diff --git a/.buildkite/package_manifest.json b/.buildkite/package_manifest.json index 63344e0083..cbd5decc6f 100644 --- a/.buildkite/package_manifest.json +++ b/.buildkite/package_manifest.json @@ -20,6 +20,7 @@ "packages/plugin-interaction-breadcrumbs", "packages/plugin-navigation-breadcrumbs", "packages/plugin-network-breadcrumbs", + "packages/plugin-network-instrumentation", "packages/plugin-simple-throttle", "packages/plugin-strip-query-string", "packages/plugin-window-onerror", @@ -97,6 +98,7 @@ "packages/delivery-react-native", "packages/plugin-console-breadcrumbs", "packages/plugin-network-breadcrumbs", + "packages/plugin-network-instrumentation", "packages/plugin-react", "packages/plugin-react-native-client-sync", "packages/plugin-react-native-event-sync", @@ -121,6 +123,7 @@ "packages/delivery-react-native", "packages/plugin-console-breadcrumbs", "packages/plugin-network-breadcrumbs", + "packages/plugin-network-instrumentation", "packages/plugin-react", "packages/plugin-react-native-client-sync", "packages/plugin-react-native-event-sync", diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ff5353dee..69c5226088 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,15 @@ ## [Unreleased] +### Added + +- (delivery-react-native) Handle request and response parameters [#2667](https://github.com/bugsnag/bugsnag-js/pull/2667) + ### Changed - (plugin-inline-script-content) Add support for additional event target constructors including MediaSource, MediaRecorder, ServiceWorker, RTCPeerConnection, and others [#2700](https://github.com/bugsnag/bugsnag-js/pull/2700) - (plugin-network-instrumentation) Manually parse URLs to improve React Native compatibility [#2674](https://github.com/bugsnag/bugsnag-js/pull/2674) +- (plugin-network-instrumentation) Refactor URL parsing to improve performance [#2667](https://github.com/bugsnag/bugsnag-js/pull/2667) ### Fixed diff --git a/packages/delivery-react-native/delivery.js b/packages/delivery-react-native/delivery.js index 97c3e86d2e..9f4f4a0215 100644 --- a/packages/delivery-react-native/delivery.js +++ b/packages/delivery-react-native/delivery.js @@ -48,6 +48,8 @@ module.exports = (client, NativeClient) => ({ breadcrumbs: derecursify(event.breadcrumbs), context: event.context, user: event._user, + request: event.request, + response: event.response, metadata: derecursify(event._metadata), groupingHash: event.groupingHash, groupingDiscriminator: event._groupingDiscriminator, diff --git a/packages/delivery-react-native/test/delivery.test.ts b/packages/delivery-react-native/test/delivery.test.ts index 109badb23f..e7b8c607d7 100644 --- a/packages/delivery-react-native/test/delivery.test.ts +++ b/packages/delivery-react-native/test/delivery.test.ts @@ -17,6 +17,8 @@ type NativeClientEvent = Pick { expect(sent[0].context).toBe('test screen') expect(sent[0].user).toEqual({ id: '123', email: undefined, name: undefined }) expect(sent[0].metadata).toEqual({}) + expect(sent[0].request).toEqual({}) + expect(sent[0].response).toEqual({}) expect(sent[0].groupingHash).toEqual('ER_GRP_098') expect(sent[0].apiKey).toBe('abcdef123456abcdef123456abcdef123456') expect(sent[0].correlation).toEqual({ traceId: 'trace-id', spanId: 'span-id' }) @@ -286,6 +290,8 @@ describe('delivery: react native', () => { expect(sent[0].context).toBe('test screen') expect(sent[0].user).toEqual({ id: '123', email: undefined, name: undefined }) expect(sent[0].metadata).toEqual({}) + expect(sent[0].request).toEqual({}) + expect(sent[0].response).toEqual({}) expect(sent[0].groupingHash).toEqual('ER_GRP_098') expect(sent[0].apiKey).toBe('abcdef123456abcdef123456abcdef123456') done() diff --git a/packages/plugin-network-instrumentation/lib/extract-domain.js b/packages/plugin-network-instrumentation/lib/extract-domain.js deleted file mode 100644 index 010febc501..0000000000 --- a/packages/plugin-network-instrumentation/lib/extract-domain.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Extract domain from URL - * @param {string} url - URL string - * @returns {string} Domain - */ -module.exports = function (url) { - try { - const isAbsolute = /^https?:\/\//i.test(url) - if (!isAbsolute) { - return 'unknown' - } - - const urlWithoutProtocol = url.replace(/^https?:\/\//i, '') - - // Find the earliest occurrence of '/', '?', or '#' to determine the domain boundary - const slashIndex = urlWithoutProtocol.indexOf('/') - const queryIndex = urlWithoutProtocol.indexOf('?') - const hashIndex = urlWithoutProtocol.indexOf('#') - let endIndex = urlWithoutProtocol.length - if (slashIndex !== -1 && slashIndex < endIndex) { - endIndex = slashIndex - } - if (queryIndex !== -1 && queryIndex < endIndex) { - endIndex = queryIndex - } - if (hashIndex !== -1 && hashIndex < endIndex) { - endIndex = hashIndex - } - - return urlWithoutProtocol.substring(0, endIndex) - } catch (e) { - return 'unknown' - } -} diff --git a/packages/plugin-network-instrumentation/lib/parse-query-params.js b/packages/plugin-network-instrumentation/lib/parse-query-params.js deleted file mode 100644 index fabb4e5222..0000000000 --- a/packages/plugin-network-instrumentation/lib/parse-query-params.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Parse query parameters from URL - * @param {string} url - URL string - * @returns {Object} Parsed query parameters - */ -module.exports = function (url) { - try { - const queryStart = url.indexOf('?') - const hashStart = url.indexOf('#') - - let queryString = '' - if (queryStart !== -1) { - const queryEnd = hashStart !== -1 && hashStart > queryStart ? hashStart : url.length - queryString = url.substring(queryStart + 1, queryEnd) - } - - // convert query string to object without using UrlSearchParams - const queryStringObject = {} - const pairs = queryString.split('&').filter(pair => pair.length > 0) - pairs.forEach(pair => { - const [key, value] = pair.split('=') - queryStringObject[decodeURIComponent(key)] = decodeURIComponent(value || '') - }) - - return queryStringObject - } catch (e) { - return {} - } -} diff --git a/packages/plugin-network-instrumentation/lib/parse-query-string.js b/packages/plugin-network-instrumentation/lib/parse-query-string.js new file mode 100644 index 0000000000..165c030db0 --- /dev/null +++ b/packages/plugin-network-instrumentation/lib/parse-query-string.js @@ -0,0 +1,19 @@ +/** + * Parse a query string into an object + * @param {string} queryString - Query string (e.g., "key=value&foo=bar") + * @returns {Object} Parsed query parameters as key-value pairs + */ +module.exports = function (queryString) { + const params = {} + if (!queryString) { + return params + } + + const pairs = queryString.split('&').filter(pair => pair.length > 0) + pairs.forEach(pair => { + const [key, value] = pair.split('=') + params[decodeURIComponent(key)] = decodeURIComponent(value || '') + }) + + return params +} diff --git a/packages/plugin-network-instrumentation/lib/parse-url.js b/packages/plugin-network-instrumentation/lib/parse-url.js new file mode 100644 index 0000000000..e3b453929f --- /dev/null +++ b/packages/plugin-network-instrumentation/lib/parse-url.js @@ -0,0 +1,64 @@ +/** + * Parse a URL in a single pass to extract domain, clean URL, and query string + * @param {string} url - URL string + * @returns {{ domain: string, cleanUrl: string, queryString: string }} Object with domain, cleanUrl, and queryString + */ +module.exports = function (url) { + try { + const isAbsolute = /^https?:\/\//i.test(url) + + // Extract query string from the full URL + const queryStart = url.indexOf('?') + const hashStart = url.indexOf('#') + + let queryString = '' + if (queryStart !== -1) { + const queryEnd = hashStart !== -1 && hashStart > queryStart ? hashStart : url.length + queryString = url.substring(queryStart + 1, queryEnd) + } + + // Extract domain + let domain = 'unknown' + if (isAbsolute) { + const urlWithoutProtocol = url.replace(/^https?:\/\//i, '') + + // Find the earliest occurrence of '/', '?', or '#' to determine the domain boundary + const slashIndex = urlWithoutProtocol.indexOf('/') + const domainQueryIndex = urlWithoutProtocol.indexOf('?') + const domainHashIndex = urlWithoutProtocol.indexOf('#') + let endIndex = urlWithoutProtocol.length + if (slashIndex !== -1 && slashIndex < endIndex) { + endIndex = slashIndex + } + if (domainQueryIndex !== -1 && domainQueryIndex < endIndex) { + endIndex = domainQueryIndex + } + if (domainHashIndex !== -1 && domainHashIndex < endIndex) { + endIndex = domainHashIndex + } + + domain = urlWithoutProtocol.substring(0, endIndex) + } + + // Strip query string while preserving hash + const hash = hashStart !== -1 ? url.substring(hashStart) : '' + let urlWithoutHash = queryStart !== -1 ? url.substring(0, queryStart) : url + if (hashStart !== -1 && queryStart === -1) { + // If there's a hash but no query string, remove the hash first + urlWithoutHash = url.substring(0, hashStart) + } + const cleanUrl = urlWithoutHash + hash + + return { + domain, + cleanUrl, + queryString + } + } catch (e) { + return { + domain: 'unknown', + cleanUrl: url, + queryString: '' + } + } +} diff --git a/packages/plugin-network-instrumentation/lib/redact-query-parameters.js b/packages/plugin-network-instrumentation/lib/redact-query-parameters.js deleted file mode 100644 index 2c36acde1f..0000000000 --- a/packages/plugin-network-instrumentation/lib/redact-query-parameters.js +++ /dev/null @@ -1,23 +0,0 @@ -const parseQueryParams = require('./parse-query-params') -const redactValues = require('./redact-values') - -module.exports = function (url, redactedKeys) { - const paramsObject = parseQueryParams(url) - const redactedParams = redactValues(paramsObject, redactedKeys) - const redactedQueryString = Object.entries(redactedParams).map(([key, value]) => `${key}=${value}`).join('&') - - const queryStart = url.indexOf('?') - const hashStart = url.indexOf('#') - const hash = hashStart !== -1 ? url.substring(hashStart) : '' - let result = queryStart !== -1 ? url.substring(0, queryStart) : url - - // Build the result URL manually - if (redactedQueryString && redactedQueryString.length > 0) { - result += '?' + redactedQueryString - } - if (hash) { - result += hash - } - - return result -} diff --git a/packages/plugin-network-instrumentation/network-instrumentation.js b/packages/plugin-network-instrumentation/network-instrumentation.js index 009b8651dd..b28d777515 100644 --- a/packages/plugin-network-instrumentation/network-instrumentation.js +++ b/packages/plugin-network-instrumentation/network-instrumentation.js @@ -3,9 +3,9 @@ * A plugin to automatically capture and report HTTP errors */ -const extractDomain = require('./lib/extract-domain') -const parseQueryParams = require('./lib/parse-query-params') -const redactQueryParameters = require('./lib/redact-query-parameters') +const parseUrl = require('./lib/parse-url') +const parseQueryString = require('./lib/parse-query-string') +const redactValues = require('./lib/redact-values') const shouldCaptureStatusCode = require('./lib/should-capture-status-code') const truncate = require('./lib/truncate') @@ -84,22 +84,25 @@ module.exports = (config = {}, global = window) => { try { // Extract request information - const url = startContext.url - const requestParams = parseQueryParams(url) + const originalUrl = startContext.url + const { domain, cleanUrl, queryString } = parseUrl(originalUrl) + const url = cleanUrl const method = startContext.method - const domain = extractDomain(url) + + // Parse query string into object + const requestParams = parseQueryString(queryString) // Create request and response objects for callback const requestObj = { - url: startContext.url, - httpMethod: startContext.method, + url, + httpMethod: method, headers: startContext.headers, - params: requestParams + params: redactValues(requestParams, client._config.redactedKeys), + bodyLength: startContext.body ? startContext.body.length : undefined } const responseObj = { statusCode: endContext.status, headers: endContext.headers, - body: endContext.body, bodyLength: endContext.body ? endContext.body.length : undefined } @@ -116,22 +119,15 @@ module.exports = (config = {}, global = window) => { // Truncate request body if (maxRequestSize > 0 && startContext.body) { requestObj.body = truncate(startContext.body, maxRequestSize) - requestObj.bodyLength = startContext.body.length } // Truncate response body - XHR only if (maxResponseSize > 0 && endContext.body) { responseObj.body = truncate(endContext.body, maxResponseSize) - responseObj.bodyLength = endContext.body.length - } - - // Strip query parameters from URL - if (requestObj.url !== '[REDACTED]') { - requestObj.url = redactQueryParameters(requestObj.url, client._config.redactedKeys) } // Create error and notify - const error = new Error(`${responseObj.statusCode}: ${requestObj.url}`) + const error = new Error(`${responseObj.statusCode}: ${url}`) error.name = 'HTTPError' const handledState = { diff --git a/packages/plugin-network-instrumentation/test/network-instrumentation-xhr.test.ts b/packages/plugin-network-instrumentation/test/network-instrumentation-xhr.test.ts index f9c64a2825..f65931ff9c 100644 --- a/packages/plugin-network-instrumentation/test/network-instrumentation-xhr.test.ts +++ b/packages/plugin-network-instrumentation/test/network-instrumentation-xhr.test.ts @@ -210,32 +210,5 @@ describe('plugin-network-instrumentation', () => { // since they don't have status codes, but let's verify the behavior expect(notifyCallbacks.length).toBe(0) }) - - it('should redact specified query parameters in XHR URLs', async () => { - const notifyCallbacks: Event[] = [] - - plugin = createPlugin({ - httpErrorCodes: { min: 400, max: 499 } - }) - - const client = new Client({ apiKey: 'api_key', plugins: [plugin], redactedKeys: ['token', 'userId'] }) - client._setDelivery(createMockDelivery(notifyCallbacks)) - - const xhr = new XMLHttpRequest() as any - xhr.status = 403 - xhr.statusText = 'Forbidden' - xhr.response = 'Forbidden' - - xhr.open('GET', 'https://api.example.com/data?userId=42') - xhr.send() - - await new Promise(resolve => setTimeout(resolve, 20)) - - expect(notifyCallbacks.length).toBe(1) - const event = notifyCallbacks[0].toJSON() - - // Verify that sensitive query parameters are redacted - expect(event.request.url).toBe('https://api.example.com/data?userId=[REDACTED]') - }) }) }) diff --git a/packages/plugin-network-instrumentation/test/network-instrumentation.test.ts b/packages/plugin-network-instrumentation/test/network-instrumentation.test.ts index e8aba988d0..33cb2fa696 100644 --- a/packages/plugin-network-instrumentation/test/network-instrumentation.test.ts +++ b/packages/plugin-network-instrumentation/test/network-instrumentation.test.ts @@ -55,6 +55,45 @@ describe('plugin-network-instrumentation', () => { }) }) + describe('config.redactedKeys', () => { + it('should redact specified query parameters from request.params', async () => { + const notifyCallbacks: Event[] = [] + + plugin = createPlugin() + + const client = new Client({ apiKey: 'api_key', plugins: [plugin], redactedKeys: ['password', 'token'] }) + client._setDelivery(createMockDelivery(notifyCallbacks)) + + mockFetch.mockResolvedValue(createMockResponse({ + ok: false, + status: 404, + statusText: 'Not Found', + url: 'https://example.com/api/users', + headers: new Headers({ 'content-type': 'application/json' }), + text: async () => '{"error": "User not found"}' + })) + + await fetch('https://example.com/api/users?page=1&limit=10&password=secret&token=abc123') + + // Wait for async processing + await new Promise(resolve => setTimeout(resolve, 10)) + + expect(notifyCallbacks.length).toBe(1) + const event = notifyCallbacks[0].toJSON() + + expect(event.exceptions[0].errorClass).toBe('HTTPError') + expect(event.exceptions[0].errorMessage).toBe('404: https://example.com/api/users') + expect(event.exceptions[0].stacktrace).toEqual([]) // Stacktrace should be empty for HTTP errors + expect(event.context).toBe('GET example.com') + expect(event.request.params).toStrictEqual({ + page: '1', + limit: '10', + password: '[REDACTED]', + token: '[REDACTED]' + }) + }) + }) + describe('config.httpErrorCodes - single range', () => { it('should capture 4xx errors when configured with single range', async () => { const notifyCallbacks: Event[] = [] @@ -313,7 +352,7 @@ describe('plugin-network-instrumentation', () => { const requestMetadata = event.request expect(requestMetadata.body).toBeUndefined() - expect(requestMetadata.bodyLength).toBeUndefined() + expect(requestMetadata.bodyLength).toBe(10000) }) }) @@ -555,7 +594,7 @@ describe('plugin-network-instrumentation', () => { expect(notifyCallbacks.length).toBe(1) const event = notifyCallbacks[0].toJSON() - expect(event.request.url).toBe('https://example.com/api/users?page=1&limit=10') + expect(event.request.url).toBe('https://example.com/api/users') expect(event.request.httpMethod).toBe('POST') expect(event.request.headers).toBeDefined() expect(event.request.headers?.['content-type']).toBe('application/json') diff --git a/packages/plugin-network-instrumentation/test/parse-query-string.test.ts b/packages/plugin-network-instrumentation/test/parse-query-string.test.ts new file mode 100644 index 0000000000..b5c61a928f --- /dev/null +++ b/packages/plugin-network-instrumentation/test/parse-query-string.test.ts @@ -0,0 +1,85 @@ +import parseQueryString from '../lib/parse-query-string' + +describe('parseQueryString', () => { + it('should parse a query string into an object', () => { + const queryString = 'token=abc123&user=john&active=true' + const result = parseQueryString(queryString) + + expect(result).toEqual({ + token: 'abc123', + user: 'john', + active: 'true' + }) + }) + + it('should handle empty query string', () => { + const result = parseQueryString('') + + expect(result).toEqual({}) + }) + + it('should handle null/undefined gracefully', () => { + // @ts-expect-error + expect(parseQueryString(null)).toEqual({}) + // @ts-expect-error + expect(parseQueryString(undefined)).toEqual({}) + }) + + it('should decode URI components', () => { + const queryString = 'email=test%40example.com&name=John%20Doe&path=%2Fhome%2Fuser' + const result = parseQueryString(queryString) + + expect(result).toEqual({ + email: 'test@example.com', + name: 'John Doe', + path: '/home/user' + }) + }) + + it('should handle empty parameter values', () => { + const queryString = 'flag&value=test&empty=' + const result = parseQueryString(queryString) + + expect(result).toEqual({ + flag: '', + value: 'test', + empty: '' + }) + }) + + it('should handle single parameter', () => { + const queryString = 'id=123' + const result = parseQueryString(queryString) + + expect(result).toEqual({ id: '123' }) + }) + + it('should skip empty pairs', () => { + const queryString = 'key=value&&&another=test' + const result = parseQueryString(queryString) + + expect(result).toEqual({ + key: 'value', + another: 'test' + }) + }) + + it('should preserve special characters in values', () => { + const queryString = 'data={"key":"value"}&text=hello%20world' + const result = parseQueryString(queryString) + + expect(result).toEqual({ + data: '{"key":"value"}', + text: 'hello world' + }) + }) + + it('should handle duplicate parameter names (last one wins)', () => { + const queryString = 'key=first&key=second&key=third' + const result = parseQueryString(queryString) + + expect(result).toEqual({ + key: 'third' + }) + }) +}) diff --git a/packages/plugin-network-instrumentation/test/parse-url.test.ts b/packages/plugin-network-instrumentation/test/parse-url.test.ts new file mode 100644 index 0000000000..65975cc54b --- /dev/null +++ b/packages/plugin-network-instrumentation/test/parse-url.test.ts @@ -0,0 +1,117 @@ +import parseUrl from '../lib/parse-url' + +describe('parseUrl', () => { + it('should extract domain, clean URL, and query string from absolute URL', () => { + const url = 'https://api.example.com/path?token=abc123&user=john' + const result = parseUrl(url) + + expect(result.domain).toBe('api.example.com') + expect(result.cleanUrl).toBe('https://api.example.com/path') + expect(result.queryString).toBe('token=abc123&user=john') + }) + + it('should handle URLs without query strings', () => { + const url = 'https://example.com/path' + const result = parseUrl(url) + + expect(result.domain).toBe('example.com') + expect(result.cleanUrl).toBe('https://example.com/path') + expect(result.queryString).toBe('') + }) + + it('should preserve hash fragments', () => { + const url = 'https://example.com/path?key=value#section' + const result = parseUrl(url) + + expect(result.domain).toBe('example.com') + expect(result.cleanUrl).toBe('https://example.com/path#section') + expect(result.queryString).toBe('key=value') + }) + + it('should handle hash without query string', () => { + const url = 'https://example.com/path#section' + const result = parseUrl(url) + + expect(result.domain).toBe('example.com') + expect(result.cleanUrl).toBe('https://example.com/path#section') + expect(result.queryString).toBe('') + }) + + it('should preserve encoded URI components in query string', () => { + const url = 'https://example.com?email=test%40example.com&name=John%20Doe' + const result = parseUrl(url) + + expect(result.domain).toBe('example.com') + expect(result.queryString).toBe('email=test%40example.com&name=John%20Doe') + }) + + it('should handle empty query parameter values', () => { + const url = 'https://example.com?flag&value=test' + const result = parseUrl(url) + + expect(result.domain).toBe('example.com') + expect(result.queryString).toBe('flag&value=test') + }) + + it('should handle multiple slashes in path', () => { + const url = 'https://example.com/api/v1/users/profile?id=123' + const result = parseUrl(url) + + expect(result.domain).toBe('example.com') + expect(result.cleanUrl).toBe('https://example.com/api/v1/users/profile') + expect(result.queryString).toBe('id=123') + }) + + it('should handle ports in domain', () => { + const url = 'https://example.com:8080/path?query=value' + const result = parseUrl(url) + + expect(result.domain).toBe('example.com:8080') + expect(result.cleanUrl).toBe('https://example.com:8080/path') + expect(result.queryString).toBe('query=value') + }) + + it('should return unknown domain for relative URLs', () => { + const url = '/api/endpoint?param=value' + const result = parseUrl(url) + + expect(result.domain).toBe('unknown') + expect(result.cleanUrl).toBe('/api/endpoint') + expect(result.queryString).toBe('param=value') + }) + + it('should handle http protocol', () => { + const url = 'http://example.com/path?key=value' + const result = parseUrl(url) + + expect(result.domain).toBe('example.com') + expect(result.cleanUrl).toBe('http://example.com/path') + expect(result.queryString).toBe('key=value') + }) + + it('should be case-insensitive for protocol matching', () => { + const url = 'HTTPS://example.com/path?key=value' + const result = parseUrl(url) + + expect(result.domain).toBe('example.com') + expect(result.cleanUrl).toBe('HTTPS://example.com/path') + expect(result.queryString).toBe('key=value') + }) + + it('should handle special characters in query string', () => { + const url = 'https://example.com?data={"key":"value"}&text=hello%20world' + const result = parseUrl(url) + + expect(result.domain).toBe('example.com') + expect(result.queryString).toBe('data={"key":"value"}&text=hello%20world') + }) + + it('should handle malformed query strings', () => { + const url = 'https://example.com/path?key=value&&&another=test' + const result = parseUrl(url) + + expect(result.domain).toBe('example.com') + expect(result.cleanUrl).toBe('https://example.com/path') + expect(result.queryString).toBe('key=value&&&another=test') + }) +}) diff --git a/packages/plugin-network-instrumentation/test/redact-query-parameters.test.ts b/packages/plugin-network-instrumentation/test/redact-query-parameters.test.ts deleted file mode 100644 index 1e5889df21..0000000000 --- a/packages/plugin-network-instrumentation/test/redact-query-parameters.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import redactQueryParameters from '../lib/redact-query-parameters' - -describe('redact-query-parameters', () => { - it('redacts specified query parameters in a URL', () => { - const url = 'http://example.com/path?token=abc123&userId=42&status=active' - const redactedKeys = ['token', 'userId'] - const redactedUrl = redactQueryParameters(url, redactedKeys) - expect(redactedUrl).toBe('http://example.com/path?token=[REDACTED]&userId=[REDACTED]&status=active') - }) - - it('handles URLs with no query parameters', () => { - const url = 'http://example.com/path' - const redactedKeys = ['token'] - const redactedUrl = redactQueryParameters(url, redactedKeys) - expect(redactedUrl).toBe('http://example.com/path') - }) - - it('handles relative URLs', () => { - const url = '/path?token=abc123&userId=42' - const redactedKeys = ['token', 'userId'] - const redactedUrl = redactQueryParameters(url, redactedKeys) - expect(redactedUrl).toBe('/path?token=[REDACTED]&userId=[REDACTED]') - }) -}) diff --git a/packages/react-native/ios/BugsnagReactNative/BugsnagEventDeserializer.m b/packages/react-native/ios/BugsnagReactNative/BugsnagEventDeserializer.m index 5c9f28c535..93ce2e30bb 100644 --- a/packages/react-native/ios/BugsnagReactNative/BugsnagEventDeserializer.m +++ b/packages/react-native/ios/BugsnagReactNative/BugsnagEventDeserializer.m @@ -30,6 +30,8 @@ - (BugsnagEvent *)deserializeEvent:(NSDictionary *)payload { threads:[self deserializeThreads:payload[@"threads"]] session:nil /* set by -[BugsnagClient notifyInternal:block:] */]; event.context = payload[@"context"]; + event.request = [self deserializeRequest:payload[@"request"]]; + event.response = [self deserializeResponse:payload[@"response"]]; event.groupingHash = payload[@"groupingHash"]; event.groupingDiscriminator = payload[@"groupingDiscriminator"]; @@ -132,4 +134,18 @@ - (BugsnagHandledState *)deserializeHandledState:(NSDictionary *)payload { return array; } +- (BugsnagHttpRequest *)deserializeRequest:(NSDictionary *)request { + if (request != nil) { + return [BugsnagHttpRequest requestFromJson:request]; + } + return nil; +} + +- (BugsnagHttpResponse *)deserializeResponse:(NSDictionary *)response { + if (response != nil) { + return [BugsnagHttpResponse responseFromJson:response]; + } + return nil; +} + @end diff --git a/test/browser/features/http_errors.feature b/test/browser/features/http_errors.feature index 4041b00c91..075a42926b 100644 --- a/test/browser/features/http_errors.feature +++ b/test/browser/features/http_errors.feature @@ -10,8 +10,8 @@ Feature: HTTP Errors Then the error is a valid browser payload for the error reporting API And I define "expected.context" as "GET " - And I define "expected.exception.message" as "401: /reflect?status=401&userId=[REDACTED]" - And I define "expected.request.url" as "/reflect?status=401&userId=[REDACTED]" + And I define "expected.exception.message" as "401: /reflect" + And I define "expected.request.url" as "/reflect" And the exception "errorClass" equals "HTTPError" And the error payload field "events.0.exceptions.0.message" equals the stored value "expected.exception.message" @@ -41,8 +41,8 @@ Feature: HTTP Errors Then the error is a valid browser payload for the error reporting API And I define "expected.context" as "POST " - And I define "expected.exception.message" as "408: /reflect?status=408&userId=[REDACTED]" - And I define "expected.request.url" as "/reflect?status=408&userId=[REDACTED]" + And I define "expected.exception.message" as "408: /reflect" + And I define "expected.request.url" as "/reflect" And the exception "errorClass" equals "HTTPError" And the error payload field "events.0.exceptions.0.message" equals the stored value "expected.exception.message" @@ -74,8 +74,8 @@ Feature: HTTP Errors Then the error is a valid browser payload for the error reporting API And I define "expected.context" as "GET " - And I define "expected.exception.message" as "404: /reflect?status=404&userId=[REDACTED]" - And I define "expected.request.url" as "/reflect?status=404&userId=[REDACTED]" + And I define "expected.exception.message" as "404: /reflect" + And I define "expected.request.url" as "/reflect" And the exception "errorClass" equals "HTTPError" And the error payload field "events.0.exceptions.0.message" equals the stored value "expected.exception.message" @@ -106,8 +106,8 @@ Feature: HTTP Errors Then the error is a valid browser payload for the error reporting API And I define "expected.context" as "POST " - And I define "expected.exception.message" as "403: /reflect?status=403&userId=[REDACTED]" - And I define "expected.request.url" as "/reflect?status=403&userId=[REDACTED]" + And I define "expected.exception.message" as "403: /reflect" + And I define "expected.request.url" as "/reflect" And the exception "errorClass" equals "HTTPError" And the error payload field "events.0.exceptions.0.message" equals the stored value "expected.exception.message" diff --git a/test/react-native/features/fixtures/expected_http_errors/401.json b/test/react-native/features/fixtures/expected_http_errors/401/exceptions.json similarity index 66% rename from test/react-native/features/fixtures/expected_http_errors/401.json rename to test/react-native/features/fixtures/expected_http_errors/401/exceptions.json index ed32018bf4..e0a16a6cef 100644 --- a/test/react-native/features/fixtures/expected_http_errors/401.json +++ b/test/react-native/features/fixtures/expected_http_errors/401/exceptions.json @@ -1,6 +1,6 @@ [ { - "message": "^401: .*\/reflect[?]status=401$", + "message": "^401: http(s)?:\/\/.*\/reflect$", "errorClass": "HTTPError", "type": "reactnativejs", "stacktrace": "IGNORE" diff --git a/test/react-native/features/fixtures/expected_http_errors/401/request.json b/test/react-native/features/fixtures/expected_http_errors/401/request.json new file mode 100644 index 0000000000..0b0c889c72 --- /dev/null +++ b/test/react-native/features/fixtures/expected_http_errors/401/request.json @@ -0,0 +1,8 @@ +{ + "url": "^http(s)?:\/\/.*\/reflect$", + "headers": {}, + "params": { + "status": "401" + }, + "httpMethod": "GET" +} diff --git a/test/react-native/features/fixtures/expected_http_errors/401/response.json b/test/react-native/features/fixtures/expected_http_errors/401/response.json new file mode 100644 index 0000000000..6cae68527b --- /dev/null +++ b/test/react-native/features/fixtures/expected_http_errors/401/response.json @@ -0,0 +1,6 @@ +{ + "body": "[Binary Data]", + "statusCode": 401, + "bodyLength": 13, + "headers": "IGNORE" +} diff --git a/test/react-native/features/fixtures/expected_http_errors/500.json b/test/react-native/features/fixtures/expected_http_errors/500/exceptions.json similarity index 66% rename from test/react-native/features/fixtures/expected_http_errors/500.json rename to test/react-native/features/fixtures/expected_http_errors/500/exceptions.json index d65420dd36..35c000eeac 100644 --- a/test/react-native/features/fixtures/expected_http_errors/500.json +++ b/test/react-native/features/fixtures/expected_http_errors/500/exceptions.json @@ -1,6 +1,6 @@ [ { - "message": "^500: .*\/reflect[?]status=500$", + "message": "^500: http(s)?:\/\/.*\/reflect$", "errorClass": "HTTPError", "type": "reactnativejs", "stacktrace": "IGNORE" diff --git a/test/react-native/features/fixtures/expected_http_errors/500/request.json b/test/react-native/features/fixtures/expected_http_errors/500/request.json new file mode 100644 index 0000000000..9f7a3f5e65 --- /dev/null +++ b/test/react-native/features/fixtures/expected_http_errors/500/request.json @@ -0,0 +1,8 @@ +{ + "url": "^http(s)?:\/\/.*\/reflect$", + "headers": {}, + "params": { + "status": "500" + }, + "httpMethod": "GET" +} diff --git a/test/react-native/features/fixtures/expected_http_errors/500/response.json b/test/react-native/features/fixtures/expected_http_errors/500/response.json new file mode 100644 index 0000000000..3b7aa2d949 --- /dev/null +++ b/test/react-native/features/fixtures/expected_http_errors/500/response.json @@ -0,0 +1,6 @@ +{ + "body": "[Binary Data]", + "statusCode": 500, + "bodyLength": 13, + "headers": "IGNORE" +} \ No newline at end of file diff --git a/test/react-native/features/http_errors.feature b/test/react-native/features/http_errors.feature index 3f59d9ce5e..a87388a67c 100644 --- a/test/react-native/features/http_errors.feature +++ b/test/react-native/features/http_errors.feature @@ -4,7 +4,9 @@ Scenario Outline: Error is reported for network requests with error status code When I run "NetworkRequestScenario" with data "" And I wait to receive an error Then the error payload field "events.0.context" matches the regex "^GET [0-9.]*:[0-9]{4}$" - And the error payload field "events.0.exceptions" matches the JSON fixture in "features/fixtures/expected_http_errors/.json" + And the error payload field "events.0.exceptions" matches the JSON fixture in "features/fixtures/expected_http_errors//exceptions.json" + And the error payload field "events.0.request" matches the JSON fixture in "features/fixtures/expected_http_errors//request.json" + And the error payload field "events.0.response" matches the JSON fixture in "features/fixtures/expected_http_errors//response.json" Examples: | status_code | | 401 | From 20f3fcc22b2d60f4bb0f84120b8c36395da01bf9 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Tue, 31 Mar 2026 16:55:30 +0100 Subject: [PATCH 48/52] ci: :construction_worker: update ci to use lts version of node (22) --- .buildkite/basic/electron-pipeline.yml | 2 +- .buildkite/basic/node-pipeline.yml | 1 + .buildkite/basic/react-native-android-full-pipeline.yml | 8 ++++---- .buildkite/basic/react-native-ios-full-pipeline.yml | 2 +- .buildkite/pipeline.yml | 8 ++++---- .github/workflows/aws-lambda.yml | 2 +- .github/workflows/pr-diff.yml | 2 +- .github/workflows/test-electron.yml | 2 +- 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/.buildkite/basic/electron-pipeline.yml b/.buildkite/basic/electron-pipeline.yml index 7881c2685d..54b0dba696 100644 --- a/.buildkite/basic/electron-pipeline.yml +++ b/.buildkite/basic/electron-pipeline.yml @@ -21,7 +21,7 @@ steps: - "^28.0.0" - "^30.0.0" node_version: - - "18" + - "22" commands: - echo "Running on Node `node -v`" - npm install electron@${ELECTRON_VERSION} --no-audit --progress=false --no-save diff --git a/.buildkite/basic/node-pipeline.yml b/.buildkite/basic/node-pipeline.yml index 05b670496d..0d9c548496 100644 --- a/.buildkite/basic/node-pipeline.yml +++ b/.buildkite/basic/node-pipeline.yml @@ -35,6 +35,7 @@ steps: - 16 - 18 - 20 + - 22 plugins: docker-compose#v4.12.0: run: node-maze-runner diff --git a/.buildkite/basic/react-native-android-full-pipeline.yml b/.buildkite/basic/react-native-android-full-pipeline.yml index 39d8b76168..465391d534 100644 --- a/.buildkite/basic/react-native-android-full-pipeline.yml +++ b/.buildkite/basic/react-native-android-full-pipeline.yml @@ -94,10 +94,10 @@ steps: key: "build-react-native-navigation-android-fixture-old-arch" timeout_in_minutes: 30 agents: - queue: macos-node-18 + queue: macos-node-22 env: JAVA_VERSION: "17" - NODE_VERSION: "18" + NODE_VERSION: "22" RN_VERSION: "{{matrix}}" RCT_NEW_ARCH_ENABLED: "0" BUILD_ANDROID: "true" @@ -119,10 +119,10 @@ steps: key: "build-react-native-navigation-android-fixture-new-arch" timeout_in_minutes: 30 agents: - queue: macos-node-18 + queue: macos-node-22 env: JAVA_VERSION: "17" - NODE_VERSION: "18" + NODE_VERSION: "22" RN_VERSION: "{{matrix}}" RCT_NEW_ARCH_ENABLED: "1" BUILD_ANDROID: "true" diff --git a/.buildkite/basic/react-native-ios-full-pipeline.yml b/.buildkite/basic/react-native-ios-full-pipeline.yml index 564f5d63b9..1ececafa64 100644 --- a/.buildkite/basic/react-native-ios-full-pipeline.yml +++ b/.buildkite/basic/react-native-ios-full-pipeline.yml @@ -93,7 +93,7 @@ steps: agents: queue: "macos-15" env: - NODE_VERSION: "18" + NODE_VERSION: "22" RN_VERSION: "{{matrix}}" RCT_NEW_ARCH_ENABLED: "0" BUILD_IOS: "true" diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index ca397c2ce0..1aa3750201 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -8,7 +8,7 @@ steps: - label: ":copyright: License Audit" timeout_in_minutes: 20 agents: - queue: "macos-node-18" + queue: "macos-node-22" command: scripts/license_finder.sh # @@ -26,9 +26,9 @@ steps: key: "publish-js" timeout_in_minutes: 10 agents: - queue: "macos-node-18" + queue: "macos-node-22" env: - NODE_VERSION: "18" + NODE_VERSION: "22" PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" command: - "bundle install" @@ -92,6 +92,6 @@ steps: # - label: ":git: Detect changed packages" agents: - queue: "macos-node-18" + queue: "macos-node-22" timeout_in_minutes: 5 command: node .buildkite/pipeline_trigger.js diff --git a/.github/workflows/aws-lambda.yml b/.github/workflows/aws-lambda.yml index 4dee97d5f9..e558492d86 100644 --- a/.github/workflows/aws-lambda.yml +++ b/.github/workflows/aws-lambda.yml @@ -31,7 +31,7 @@ jobs: - name: Install Node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: - node-version: 18 + node-version: 22 - name: Run tests run: | diff --git a/.github/workflows/pr-diff.yml b/.github/workflows/pr-diff.yml index 3efa504965..6d86405744 100644 --- a/.github/workflows/pr-diff.yml +++ b/.github/workflows/pr-diff.yml @@ -11,7 +11,7 @@ jobs: - name: Setup node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: - node-version: 18.x + node-version: 22 - name: Checkout base branch uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/test-electron.yml b/.github/workflows/test-electron.yml index 607ab784ba..cd21efe306 100644 --- a/.github/workflows/test-electron.yml +++ b/.github/workflows/test-electron.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: electron: [ '^20.0.0', '^24.0.0', '^26.0.0', '^28.0.0', '^30.0.0' ] - node-version: [18] + node-version: [22] os: [ ubuntu-latest ] steps: From 47cb457cbb653008e18ece5aa4a8c089dc4ec7af Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 1 Apr 2026 09:37:11 +0100 Subject: [PATCH 49/52] pin build to 22 [full ci] --- .buildkite/basic/react-native-android-pipeline.yml | 1 + .buildkite/basic/react-native-ios-pipeline.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.buildkite/basic/react-native-android-pipeline.yml b/.buildkite/basic/react-native-android-pipeline.yml index 14a40bec09..7a3382f5ea 100644 --- a/.buildkite/basic/react-native-android-pipeline.yml +++ b/.buildkite/basic/react-native-android-pipeline.yml @@ -13,6 +13,7 @@ steps: env: JAVA_VERSION: "17" RN_VERSION: "{{matrix}}" + NODE_VERSION: "22" RCT_NEW_ARCH_ENABLED: "1" BUILD_ANDROID: "true" REACT_NAVIGATION: "true" diff --git a/.buildkite/basic/react-native-ios-pipeline.yml b/.buildkite/basic/react-native-ios-pipeline.yml index 5522511efd..4b0f50c3ae 100644 --- a/.buildkite/basic/react-native-ios-pipeline.yml +++ b/.buildkite/basic/react-native-ios-pipeline.yml @@ -16,6 +16,7 @@ steps: queue: "macos-15" env: RN_VERSION: "{{matrix}}" + NODE_VERSION: "22" RCT_NEW_ARCH_ENABLED: "1" BUILD_IOS: "true" XCODE_VERSION: "16.2.0" From 45b07e9b7fcf4caae9d5507fc3eec942c5cd29b6 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Thu, 2 Apr 2026 11:17:23 +0100 Subject: [PATCH 50/52] update dockerfiles to use node 22 [full ci] --- dockerfiles/Dockerfile.browser | 2 +- dockerfiles/Dockerfile.ci | 2 +- dockerfiles/Dockerfile.node | 2 +- dockerfiles/Dockerfile.release | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dockerfiles/Dockerfile.browser b/dockerfiles/Dockerfile.browser index 509341d649..d5f64fed54 100644 --- a/dockerfiles/Dockerfile.browser +++ b/dockerfiles/Dockerfile.browser @@ -1,5 +1,5 @@ # CI test image for unit/lint/type tests -FROM node:18-alpine@sha256:974afb6cbc0314dc6502b14243b8a39fbb2d04d975e9059dd066be3e274fbb25 AS browser-feature-builder +FROM node:22-alpine@sha256:4d64b49e6c891c8fc821007cb1cdc6c0db7773110ac2c34bf2e6960adef62ed3 AS browser-feature-builder RUN apk add --update bash python3 make gcc g++ musl-dev xvfb-run curl diff --git a/dockerfiles/Dockerfile.ci b/dockerfiles/Dockerfile.ci index a141b16590..112d858bec 100644 --- a/dockerfiles/Dockerfile.ci +++ b/dockerfiles/Dockerfile.ci @@ -1,5 +1,5 @@ # CI test image for unit/lint/type tests -FROM node:18-alpine@sha256:974afb6cbc0314dc6502b14243b8a39fbb2d04d975e9059dd066be3e274fbb25 +FROM node:22-alpine@sha256:4d64b49e6c891c8fc821007cb1cdc6c0db7773110ac2c34bf2e6960adef62ed3 RUN apk add --update bash python3 make gcc g++ musl-dev xvfb-run curl diff --git a/dockerfiles/Dockerfile.node b/dockerfiles/Dockerfile.node index afe9ee7ef4..479984bb7e 100644 --- a/dockerfiles/Dockerfile.node +++ b/dockerfiles/Dockerfile.node @@ -1,5 +1,5 @@ # CI test image for unit/lint/type tests -FROM node:18-alpine@sha256:974afb6cbc0314dc6502b14243b8a39fbb2d04d975e9059dd066be3e274fbb25 AS node-feature-builder +FROM node:22-alpine@sha256:4d64b49e6c891c8fc821007cb1cdc6c0db7773110ac2c34bf2e6960adef62ed3 AS node-feature-builder RUN apk add --update bash python3 make gcc g++ musl-dev xvfb-run curl diff --git a/dockerfiles/Dockerfile.release b/dockerfiles/Dockerfile.release index 6927d72e21..1c59a1fb2f 100644 --- a/dockerfiles/Dockerfile.release +++ b/dockerfiles/Dockerfile.release @@ -1,4 +1,4 @@ -FROM node:18-alpine@sha256:974afb6cbc0314dc6502b14243b8a39fbb2d04d975e9059dd066be3e274fbb25 +FROM node:22-alpine@sha256:4d64b49e6c891c8fc821007cb1cdc6c0db7773110ac2c34bf2e6960adef62ed3 COPY ./zscaler-root-ca.crt* /usr/local/share/ca-certificates/ RUN if [ -f /usr/local/share/ca-certificates/zscaler-root-ca.crt ]; then cat /usr/local/share/ca-certificates/zscaler-root-ca.crt >> /etc/ssl/certs/ca-certificates.crt; fi From 25003b546b2231a033291a335118206b1b0f572f Mon Sep 17 00:00:00 2001 From: SB-priyankap Date: Tue, 7 Apr 2026 18:45:52 +0530 Subject: [PATCH 51/52] release 8.9.0 minor version --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69c5226088..5a81c5c72c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [Unreleased] +## [8.9.0] - 2026-04-07 ### Added From 356222b4c5d3952c7998238a34c2f0d8d3083f52 Mon Sep 17 00:00:00 2001 From: SB-priyankap Date: Tue, 7 Apr 2026 20:01:36 +0530 Subject: [PATCH 52/52] release 8.9.0 minor version --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a81c5c72c..81c59aa24a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [8.9.0] - 2026-04-07 +## [8.9.0] - 2026-04-08 ### Added