Skip to content

feat(tracing): Wrap Expo Router push, replace, navigate, back, dismiss in addition to prefetch#6221

Draft
alwx wants to merge 5 commits into
mainfrom
alwx/feature/wrap-expo-router-methods
Draft

feat(tracing): Wrap Expo Router push, replace, navigate, back, dismiss in addition to prefetch#6221
alwx wants to merge 5 commits into
mainfrom
alwx/feature/wrap-expo-router-methods

Conversation

@alwx
Copy link
Copy Markdown
Contributor

@alwx alwx commented May 27, 2026

📢 Type of change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring

📜 Description

Extends the Expo Router instrumentation beyond prefetch so that programmatic navigation calls produce breadcrumbs and spans, and the resulting idle navigation transaction can be attributed back to the call that triggered it.

What changed:

  • wrapExpoRouter now wraps push, replace, navigate, back, and dismiss in addition to prefetch.
  • Each wrapped call:
    • emits a navigation breadcrumb (category: 'navigation', data.method, data.pathname),
    • opens a short-lived navigation.<method> span tagged with sentry.origin = auto.navigation.expo_router, navigation.method, and route.name,
    • stores a pending hand-off (pendingExpoRouterNavigation) that the next React Navigation idle navigation span consumes to set its own navigation.method attribute, attributing the transaction back to the originating call.
  • A single __sentryWrapped flag guards against double-wrapping if wrapExpoRouter is called multiple times on the same router.
  • PII handling mirrors reactnavigation.ts: raw href and route params (which can encode user identifiers like /users/123 or { id: '123' }) are only attached to breadcrumbs and span attributes when sendDefaultPii is enabled. method, pathname (the route template), and route.name remain unconditional so navigations stay visible and groupable.

💡 Motivation and Context

Fixes #6158.

Previously only prefetch was instrumented, so any programmatic router.push(...) / router.replace(...) / router.back() etc. was invisible in Sentry — no breadcrumb trail, no span, and the resulting React Navigation idle transaction had no way to know which call site initiated it. This PR closes that gap while keeping the existing prefetch behavior intact and aligning PII handling with the rest of the navigation tracing.

💚 How did you test it?

  • Added unit tests in packages/core/test/tracing/expoRouter.test.ts covering:
    • prefetch (string href, object href, object without pathname, sync + async success, sync + async errors),
    • push / replace / navigate via describe.each — span and breadcrumb shape with string and object hrefs, error reporting, and the pending-navigation hand-off,
    • back() and dismiss(count) with no/optional args,
    • double-wrap idempotency,
    • PII gating: default-off assertions (no href, no params) and dedicated sendDefaultPii: true cases that verify href and params reappear on both the breadcrumb and the span.
  • Full JS test suite: yarn jest — 113 suites / 1481 tests passing.
  • Linters: yarn lint:lerna clean.
  • API report: yarn api-report:check up to date.

📝 Checklist

  • I added tests to verify changes
  • No new PII added or SDK only sends newly added PII if sendDefaultPii is enabled
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • All tests passing
  • No breaking changes

🔮 Next steps

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 27, 2026

Semver Impact of This PR

None (no version bump detected)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


  • feat(tracing): Wrap Expo Router push, replace, navigate, back, dismiss in addition to prefetch by alwx in #6221
  • feat(logs): Add enableAutoConsoleLogs option to opt out of console capture by alwx in #6235
  • chore(deps): update JavaScript SDK to v10.55.0 by github-actions in #6222
  • chore(deps): update Sentry Android Gradle Plugin to v6.9.0 by github-actions in #6230
  • refactor(android): Convert sentry.gradle to Kotlin DSL (sentry.gradle.kts) by antonis in #6119

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 27, 2026

Fails
🚫 Pull request is not ready for merge, please add the "ready-to-merge" label to the pull request
Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against a88474b

Comment thread packages/core/src/js/tracing/expoRouter.ts
@alwx alwx changed the title Expo Router: wrap push, replace, navigate, back, dismiss in addition to prefetch WIP: Expo Router: wrap push, replace, navigate, back, dismiss in addition to prefetch May 28, 2026
…nd sendDefaultPii

Expo Router `push`/`replace`/`navigate`/`back`/`dismiss` (and `prefetch`)
wrappers previously emitted the raw `href` and route `params` on both
the navigation breadcrumb and span attributes regardless of the client
options. Dynamic segments like `/users/123` and parameter values such
as `{ id: '123' }` can carry user identifiers and other PII, so the
existing `reactnavigation.ts` integration already gates them behind
`sendDefaultPii`. The new Expo Router wrappers should follow the same
contract.

This change:

- Reads `sendDefaultPii` from the client options and only attaches
  `href` (raw string or stringified object form) and `params` to the
  breadcrumb `data` and span attributes when it is enabled.
- Keeps the non-PII fields — `method`, `pathname` (route template),
  and `route.name` — unconditional so navigations remain visible and
  groupable in Sentry without leaking user data.
- Applies the same guard to the `prefetch` span's `route.href`
  attribute, which was newly added in this PR.
- Adds dedicated `sendDefaultPii` test cases and updates the existing
  default-off assertions for all wrapped methods.
- Adds a changelog entry for #6221.
@alwx alwx changed the title WIP: Expo Router: wrap push, replace, navigate, back, dismiss in addition to prefetch feat(tracing): Wrap Expo Router push, replace, navigate, back, dismiss in addition to prefetch May 28, 2026
Comment thread packages/core/src/js/tracing/expoRouter.ts
Comment thread packages/core/src/js/tracing/expoRouter.ts
Comment thread packages/core/src/js/tracing/expoRouter.ts
SPAN_ORIGIN_AUTO_EXPO_ROUTER_NAVIGATION,
SPAN_ORIGIN_AUTO_EXPO_ROUTER_PREFETCH,
} from '../../src/js/tracing/origin';
import {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No test covers reactnavigation.ts consuming pendingExpoRouterNavigation to tag idle spans

The PR's core feature — attributing idle navigation spans back to the Expo Router call via navigation.method — is exercised in expoRouter.test.ts only for the setter side; no test in reactnavigation.test.ts (or anywhere else) verifies that reactnavigation.ts actually reads and applies the pending value to the resulting span.

Evidence
  • reactnavigation.ts:456 calls consumePendingExpoRouterNavigation() and sets navigation.method on latestNavigationSpan.
  • grep for pendingExpoRouter and navigation.method across all test files returns only hits inside expoRouter.test.ts.
  • reactnavigation.test.ts has zero matches for either symbol.
  • The hand-off path (set in wrapNavigationMethod → consumed in reactnavigation idle span handler) has no integration-level test, so a regression in the consumer would go undetected.
Also found at 2 additional locations
  • packages/core/src/js/tracing/reactnavigation.ts:33
  • packages/core/src/js/tracing/reactnavigation.ts:455

Identified by Warden code-review · 4UA-DHD

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wrap push, replace, navigate, back, dismiss in addition to prefetch

1 participant