@grafana/faro-react-native 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +80 -3
  2. package/android/build.gradle +145 -0
  3. package/dist/cjs/config/getRNInstrumentations.js +3 -1
  4. package/dist/cjs/config/getRNInstrumentations.js.map +1 -1
  5. package/dist/cjs/config/makeRNConfig.js +7 -4
  6. package/dist/cjs/config/makeRNConfig.js.map +1 -1
  7. package/dist/cjs/config/types.js.map +1 -1
  8. package/dist/cjs/generated/faroRNPackageMeta.js +1 -1
  9. package/dist/cjs/generated/faroRNPackageMeta.js.map +1 -1
  10. package/dist/cjs/instrumentations/errors/index.js +3 -2
  11. package/dist/cjs/instrumentations/errors/index.js.map +1 -1
  12. package/dist/cjs/instrumentations/errors/stackTraceParser.js +98 -37
  13. package/dist/cjs/instrumentations/errors/stackTraceParser.js.map +1 -1
  14. package/dist/cjs/util/performanceObserverPolyfill.js +32 -3
  15. package/dist/cjs/util/performanceObserverPolyfill.js.map +1 -1
  16. package/dist/esm/config/getRNInstrumentations.js +3 -1
  17. package/dist/esm/config/getRNInstrumentations.js.map +1 -1
  18. package/dist/esm/config/makeRNConfig.js +7 -4
  19. package/dist/esm/config/makeRNConfig.js.map +1 -1
  20. package/dist/esm/config/types.js.map +1 -1
  21. package/dist/esm/generated/faroRNPackageMeta.js +1 -1
  22. package/dist/esm/generated/faroRNPackageMeta.js.map +1 -1
  23. package/dist/esm/instrumentations/errors/index.js +3 -2
  24. package/dist/esm/instrumentations/errors/index.js.map +1 -1
  25. package/dist/esm/instrumentations/errors/stackTraceParser.js +97 -37
  26. package/dist/esm/instrumentations/errors/stackTraceParser.js.map +1 -1
  27. package/dist/esm/util/performanceObserverPolyfill.js +32 -3
  28. package/dist/esm/util/performanceObserverPolyfill.js.map +1 -1
  29. package/dist/types/config/types.d.ts +5 -0
  30. package/dist/types/generated/faroRNPackageMeta.d.ts +1 -1
  31. package/dist/types/instrumentations/errors/index.d.ts +5 -0
  32. package/dist/types/instrumentations/errors/stackTraceParser.d.ts +19 -3
  33. package/dist/types/util/performanceObserverPolyfill.d.ts +5 -2
  34. package/ios/faro-upload-composed-source-map.sh +87 -0
  35. package/package.json +7 -10
  36. package/react-native.config.js +9 -1
  37. package/src/config/getRNInstrumentations.ts +5 -1
  38. package/src/config/makeRNConfig.test.ts +11 -0
  39. package/src/config/makeRNConfig.ts +7 -4
  40. package/src/config/types.ts +5 -0
  41. package/src/generated/faroRNPackageMeta.ts +1 -1
  42. package/src/initialize.test.ts +25 -0
  43. package/src/instrumentations/errors/index.ts +25 -8
  44. package/src/instrumentations/errors/stackTraceParser.test.ts +65 -0
  45. package/src/instrumentations/errors/stackTraceParser.ts +121 -37
  46. package/src/util/performanceObserverPolyfill.test.ts +48 -0
  47. package/src/util/performanceObserverPolyfill.ts +43 -3
package/README.md CHANGED
@@ -47,7 +47,12 @@ initializeFaro({
47
47
  fetchVitalsInterval: 30000, // optional, ms
48
48
  frameMonitoringOptions: {}, // optional, when refreshRateVitals true
49
49
  enableTracing: false, // optional, requires @grafana/faro-react-native-tracing
50
- tracingOptions: {}, // optional
50
+ tracingOptions: {
51
+ instrumentationOptions: {
52
+ // Traces fetch() by default. Enable this for axios/direct XHR apps.
53
+ enableXhrInstrumentation: false,
54
+ },
55
+ }, // optional
51
56
 
52
57
  // Instrumentation options - optional
53
58
  consoleCaptureOptions: {},
@@ -74,6 +79,78 @@ initializeFaro({
74
79
  });
75
80
  ```
76
81
 
82
+ ## Error symbolication
83
+
84
+ This section ties together **SDK configuration**, **release builds**, **uploaded source maps**, and **errors/crashes** so stacks from Hermes release binaries show up as your real source files in Grafana.
85
+
86
+ ### What you configure in the app
87
+
88
+ 1. **`initializeFaro({ app: { name: '…' }, … })`** — `app.name` must match **`appName`** in [@grafana/faro-metro-plugin](https://www.npmjs.com/package/@grafana/faro-metro-plugin) (`metro.config.js`).
89
+ 2. **`releaseBundleFilename`** (e.g. `index.android.bundle` or `main.jsbundle`) — must match the **bundle basename** your composed source map uses in its top-level **`file`** field (same as Metro’s `sourceMapFile` when you override it). This keeps stack **filenames** aligned with the map the backend will look up.
90
+ 3. **Metro** — wrap config with **`withFaroConfig`** so the release bundle gets the Faro preamble and the right map shape for Hermes.
91
+
92
+ See [Android release upload](#error-symbolication-android-release-upload) and [iOS release upload](#error-symbolication-ios-release-upload) below.
93
+
94
+ You do **not** set `app.bundleId` in `initializeFaro` for this flow.
95
+
96
+ ### What happens when you ship a new release
97
+
98
+ For **Android** and **iOS** release builds, the native pipeline (Metro → Hermes → **`compose-source-maps.js`**) produces a single **composed** `.map` file for that binary. **`@grafana/faro-react-native`** autolinking runs **`faro-upload-source-map`** (→ **`faro-cli metro upload`**) after that file exists, as long as you provide **`FARO_BUNDLE_ID`** and the **`FARO_SOURCEMAP_*`** variables (see [Android](#error-symbolication-android-release-upload) / [iOS](#error-symbolication-ios-release-upload) below). The map is stored in Grafana’s source map API **keyed by that bundle id**.
99
+
100
+ The Metro preamble, baked in at bundle time, sets a global like **`globalThis['__faroBundleId_<appName>']`** to the same **`FARO_BUNDLE_ID`** value so runtime telemetry can reference the correct map record.
101
+
102
+ ### What happens at runtime (errors and crashes)
103
+
104
+ When the app starts, **`@grafana/faro-core`** runs **`getBundleId(app.name)`** during meta registration. That helper reads the preamble global above (see [getBundleId](https://github.com/grafana/faro-web-sdk/blob/main/packages/core/src/utils/sourceMaps.ts) in **`@grafana/faro-core`**) and sets **`meta.app.bundleId`** on outgoing Faro payloads. You get a stable link between “`this app binary`” and “`this uploaded map`” without hard-coding the id in JS.
105
+
106
+ When the SDK reports an error or crash, it parses the stack into **structured frames** (`filename`, `lineno`, `colno`, …). Those values describe positions **in the shipped JS bundle / Hermes stack space** (and filenames normalized to match the map, e.g. stripping `address at …` prefixes so **`frame.filename`** matches the map’s **`file`** field). The SDK **does not** load the `.map` on the device and **does not** rewrite frames to `src/**/*.tsx` paths inside the payload sent to the collector.
107
+
108
+ ### How those frames connect to your real source files
109
+
110
+ **In Grafana (Frontend Observability)**, the ingest stack resolves frames using the **composed** source map whose **`bundleId`** matches **`meta.app.bundleId`**. That step turns bundle-relative **line/column** (and the bundle **`file`** name) into human-readable paths such as **`src/...tsx`** in the UI. If **`meta.app.bundleId`** is missing or wrong, or the wrong map was uploaded, symbolication will not line up with this build.
111
+
112
+ So end-to-end: **same bundle id** in the binary preamble, in **`meta.app.bundleId`**, and in the source map upload → **correct map lookup** → **readable stacks**.
113
+
114
+ ---
115
+
116
+ ### Error symbolication: Android release upload
117
+
118
+ Hermes release builds produce the composed map at:
119
+
120
+ `app/build/generated/sourcemaps/react/release/index.android.bundle.map`
121
+
122
+ This package’s **`android/build.gradle`** registers **`faroUploadComposedSourceMapAndroidRelease`** on your **`:app`** project and finalizes **`bundleReleaseJsAndAssets`** / **`createBundleReleaseJsAndAssets`**.
123
+
124
+ **Setup:**
125
+
126
+ 1. `npm install @grafana/faro-react-native @grafana/faro-metro-plugin`
127
+ 2. Wrap **`metro.config.js`** with `withFaroConfig({...}, faroOpts)` (see the metro plugin README).
128
+ 3. Export **`FARO_BUNDLE_ID`** and **`FARO_SOURCEMAP_*`** (`FARO_SOURCEMAP_ENDPOINT`, `FARO_SOURCEMAP_APP_ID`, `FARO_SOURCEMAP_STACK_ID`, `FARO_SOURCEMAP_API_KEY`) before release builds.
129
+ 4. `yarn android --mode=release` (or `installRelease` / `assembleRelease` / `bundleRelease`).
130
+
131
+ **No `android/app/build.gradle` edits** are required. The task runs **`node_modules/@grafana/faro-metro-plugin/bin/faro-upload-source-map.js`**. Use **`FARO_SKIP_SOURCEMAP_UPLOAD=1`** to skip upload when iterating offline.
132
+
133
+ ### Error symbolication: iOS release upload
134
+
135
+ **`pod install`** adds **`[Faro] Upload composed source map (Release)`** (from **`react-native.config.js`**). On **Release**, it runs **`ios/faro-upload-composed-source-map.sh`**, which invokes the same shim after the composed **`main.jsbundle.map`** exists.
136
+
137
+ **Setup:**
138
+
139
+ 1. Same installs and **`metro.config.js`** as Android.
140
+ 2. Ensure **`SOURCEMAP_FILE`** is set when **`react-native-xcode.sh`** runs (Release). React Native has **no default**; without it, **`main.jsbundle.map`** is not written. Prefer **`ios/.xcode.env`** or **`ios/.xcode.env.local`** (sourced before bundling):
141
+
142
+ ```sh
143
+ export SOURCEMAP_FILE="${DERIVED_FILE_DIR}/main.jsbundle.map"
144
+ ```
145
+
146
+ **`DERIVED_FILE_DIR`** is set by Xcode for the bundle phase. Alternatively, set the same value as a **User-Defined** build setting on the app target (`SOURCEMAP_FILE = $(DERIVED_FILE_DIR)/main.jsbundle.map`). Exporting **`SOURCEMAP_FILE`** only in an outer shell before `yarn ios` is unreliable because **`${DERIVED_FILE_DIR}`** is not your shell’s variable.
147
+
148
+ 3. Export the same **`FARO_*`** variables for the Release build environment.
149
+
150
+ **Debug** skips upload. **`FARO_SKIP_SOURCEMAP_UPLOAD=1`** skips on Release when needed.
151
+
152
+ **Manual / CI:** run **`faro-cli metro upload`** with **`--map`** pointing at the **composed** map and the same flags.
153
+
77
154
  ## Features
78
155
 
79
156
  ### Core Instrumentations
@@ -85,8 +162,8 @@ initializeFaro({
85
162
  - **App State Instrumentation** - Tracks when app goes to background/foreground
86
163
  - **Performance Instrumentation** - Monitors CPU usage, memory usage, and app startup time using native OS APIs
87
164
  - **Startup Instrumentation** - Automatically tracks app startup duration from process start
88
- - **HTTP Instrumentation** - Tracks HTTP requests (fetch) and correlates them with user actions
89
- - **XHR Instrumentation** - Tracks XMLHttpRequest calls (replaced by TracingInstrumentation when `enableTracing` is true)
165
+ - **HTTP Instrumentation** - Tracks `fetch()` requests and correlates them with user actions
166
+ - **XHR Instrumentation** - Tracks `XMLHttpRequest` calls when tracing is disabled; with `enableTracing: true`, enable XHR tracing via `tracingOptions.instrumentationOptions.enableXhrInstrumentation`
90
167
 
91
168
  **Enabled by default** (opt-out via config):
92
169
 
@@ -43,3 +43,148 @@ dependencies {
43
43
  implementation 'com.facebook.react:react-native:+'
44
44
  implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.8.0'
45
45
  }
46
+
47
+ /* ---------------------------------------------------------------------------
48
+ * Faro: composed Hermes source-map upload (Android release).
49
+ *
50
+ * Because @grafana/faro-react-native is autolinked by React Native CLI, this
51
+ * file is applied automatically — the consumer app needs ZERO edits to
52
+ * `android/app/build.gradle`. Setup for the customer is:
53
+ *
54
+ * 1. npm install @grafana/faro-react-native @grafana/faro-metro-plugin
55
+ * 2. Wrap metro.config.js with `withFaroConfig(...)`
56
+ * 3. Export FARO_BUNDLE_ID + FARO_SOURCEMAP_* before running
57
+ * `yarn android --mode=release`.
58
+ *
59
+ * On any release-style command (assembleRelease, bundleRelease, installRelease,
60
+ * `yarn android --mode=release`), release entry tasks depend on
61
+ * `:app:faroUploadComposedSourceMapAndroidRelease`, which in turn depends on
62
+ * the JS bundle task (`bundleReleaseJsAndAssets` or `createBundleReleaseJsAndAssets`)
63
+ * so upload runs only after a successful bundle — never via a finalizer (which
64
+ * can run even when bundling fails and could upload a stale map). The upload
65
+ * task runs `faro-cli metro upload` via a small Node shim. Gradle reads every
66
+ * required setting from env vars and passes them as **explicit CLI arguments** so the CLI never has to fall
67
+ * back to its own env-var resolution from this entry point — making the
68
+ * contract between Gradle and the CLI fully visible in the command line that
69
+ * you'll see in `--info` build output.
70
+ *
71
+ * Required env at build time (Gradle forwards each as a CLI flag):
72
+ * FARO_BUNDLE_ID — must match the id baked into the JS bundle
73
+ * preamble by @grafana/faro-metro-plugin so
74
+ * meta.app.bundleId on the device matches
75
+ * the uploaded map. Forwarded as --bundle-id.
76
+ * FARO_SOURCEMAP_ENDPOINT — Forwarded as --endpoint.
77
+ * FARO_SOURCEMAP_APP_ID — Forwarded as --app-id.
78
+ * FARO_SOURCEMAP_STACK_ID — Forwarded as --stack-id.
79
+ * FARO_SOURCEMAP_API_KEY — Forwarded as --api-key.
80
+ *
81
+ * Optional env at build time:
82
+ * FARO_SKIP_SOURCEMAP_UPLOAD=1 — skip the upload (e.g. offline iteration).
83
+ *
84
+ * Implementation notes:
85
+ * - We register and wire the task on the consumer's `:app` project (not on
86
+ * this library project), because `:app` is the one that runs
87
+ * `bundleReleaseJsAndAssets`.
88
+ * - We do all of that inside `gradle.projectsEvaluated` so all subprojects
89
+ * and their tasks are visible by the time we wire `dependsOn`.
90
+ * - All preconditions (shim present, composed map present, every required
91
+ * env var set) are checked in `onlyIf` so the build never hard-fails on a
92
+ * missing source-map upload — it just logs a warning and moves on.
93
+ * - We invoke `node_modules/@grafana/faro-metro-plugin/bin/faro-upload-source-map.js`
94
+ * (a thin shim that forwards to `faro-cli metro upload`). Going through
95
+ * the shim by absolute path keeps this hook insensitive to how the
96
+ * consumer's package manager hoists `@grafana/faro-cli`.
97
+ *
98
+ * Advanced users who don't install @grafana/faro-react-native can apply
99
+ * `node_modules/@grafana/faro-metro-plugin/android/source-map-upload.gradle`
100
+ * manually from their app's `android/app/build.gradle` instead.
101
+ * --------------------------------------------------------------------------- */
102
+ gradle.projectsEvaluated {
103
+ def appProject = rootProject.allprojects.find { it.path == ":app" }
104
+ if (appProject == null) {
105
+ logger.info("[Faro] :app project not found — skipping composed source map upload wiring.")
106
+ return
107
+ }
108
+
109
+ def jsRoot = rootProject.projectDir.parentFile
110
+ def cliPath = new File(jsRoot, "node_modules/@grafana/faro-metro-plugin/bin/faro-upload-source-map.js")
111
+ def composedMap = new File(appProject.buildDir, "generated/sourcemaps/react/release/index.android.bundle.map")
112
+
113
+ def uploadTask = appProject.tasks.register("faroUploadComposedSourceMapAndroidRelease", Exec) {
114
+ group = "react"
115
+ description = "Upload the composed Hermes Android source map to Faro."
116
+
117
+ workingDir = jsRoot
118
+
119
+ def faroSkip = (System.getenv("FARO_SKIP_SOURCEMAP_UPLOAD") ?: "").trim().toLowerCase()
120
+ def faroBundleId = (System.getenv("FARO_BUNDLE_ID") ?: "").trim()
121
+ def faroEndpoint = (System.getenv("FARO_SOURCEMAP_ENDPOINT") ?: "").trim()
122
+ def faroAppId = (System.getenv("FARO_SOURCEMAP_APP_ID") ?: "").trim()
123
+ def faroStackId = (System.getenv("FARO_SOURCEMAP_STACK_ID") ?: "").trim()
124
+ def faroApiKey = (System.getenv("FARO_SOURCEMAP_API_KEY") ?: "").trim()
125
+
126
+ // Capture the inputs we use for caching / up-to-date checks. We
127
+ // deliberately *don't* track FARO_SOURCEMAP_API_KEY in inputs to
128
+ // avoid Gradle leaking it into a cache-key string.
129
+ inputs.file(composedMap).withPropertyName("composedMap").optional()
130
+ inputs.property("faroBundleId", faroBundleId)
131
+ inputs.property("faroEndpoint", faroEndpoint)
132
+ inputs.property("faroAppId", faroAppId)
133
+ inputs.property("faroStackId", faroStackId)
134
+ inputs.property("faroSkipUpload", faroSkip)
135
+
136
+ onlyIf {
137
+ if (faroSkip == "1" || faroSkip == "true") {
138
+ logger.lifecycle("[Faro] Skipping composed source map upload (FARO_SKIP_SOURCEMAP_UPLOAD=${faroSkip}).")
139
+ return false
140
+ }
141
+ if (!cliPath.exists()) {
142
+ logger.warn("[Faro] Skipping composed source map upload — CLI shim not found at ${cliPath}. Install @grafana/faro-metro-plugin alongside @grafana/faro-react-native.")
143
+ return false
144
+ }
145
+ if (!composedMap.exists()) {
146
+ logger.warn("[Faro] Skipping composed source map upload — ${composedMap} does not exist. Did Hermes precompile run?")
147
+ return false
148
+ }
149
+ def missing = []
150
+ if (faroBundleId.isEmpty()) missing << "FARO_BUNDLE_ID"
151
+ if (faroEndpoint.isEmpty()) missing << "FARO_SOURCEMAP_ENDPOINT"
152
+ if (faroAppId.isEmpty()) missing << "FARO_SOURCEMAP_APP_ID"
153
+ if (faroStackId.isEmpty()) missing << "FARO_SOURCEMAP_STACK_ID"
154
+ if (faroApiKey.isEmpty()) missing << "FARO_SOURCEMAP_API_KEY"
155
+ if (!missing.isEmpty()) {
156
+ logger.warn("[Faro] Skipping composed source map upload — missing required env vars: ${missing.join(", ")}. Export them before running `yarn android --mode=release`. FARO_BUNDLE_ID must match the id baked into the bundle preamble by @grafana/faro-metro-plugin.")
157
+ return false
158
+ }
159
+ return true
160
+ }
161
+
162
+ commandLine(
163
+ "node", cliPath.absolutePath,
164
+ "--verbose",
165
+ "--map", composedMap.absolutePath,
166
+ "--endpoint", faroEndpoint,
167
+ "--app-id", faroAppId,
168
+ "--stack-id", faroStackId,
169
+ "--api-key", faroApiKey,
170
+ "--bundle-id", faroBundleId,
171
+ )
172
+ }
173
+
174
+ def bundleTaskNames = ["bundleReleaseJsAndAssets", "createBundleReleaseJsAndAssets"]
175
+ uploadTask.configure { task ->
176
+ bundleTaskNames.each { name ->
177
+ def bundleTask = appProject.tasks.findByName(name)
178
+ if (bundleTask != null) {
179
+ task.dependsOn(bundleTask)
180
+ }
181
+ }
182
+ }
183
+
184
+ ["assembleRelease", "bundleRelease", "installRelease"].each { name ->
185
+ def releaseTask = appProject.tasks.findByName(name)
186
+ if (releaseTask != null) {
187
+ releaseTask.dependsOn(uploadTask)
188
+ }
189
+ }
190
+ }
@@ -69,7 +69,9 @@ function getRNInstrumentations(config) {
69
69
  var instrumentations = [];
70
70
  // Error reporting (Flutter: enableFlutterErrorReporting)
71
71
  if (enableErrorReporting) {
72
- instrumentations.push(new errors_1.ErrorsInstrumentation());
72
+ instrumentations.push(new errors_1.ErrorsInstrumentation({
73
+ releaseBundleFilename: config.releaseBundleFilename,
74
+ }));
73
75
  }
74
76
  // Console capture - not in Flutter SDK, RN-specific
75
77
  if (enableConsoleCapture) {
@@ -1 +1 @@
1
- {"version":3,"file":"getRNInstrumentations.js","sourceRoot":"","sources":["../../../src/config/getRNInstrumentations.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\n\nimport type { Instrumentation, Patterns } from '@grafana/faro-core';\n\nimport { ANRInstrumentation } from '../instrumentations/anr';\nimport { AppStateInstrumentation } from '../instrumentations/appState';\nimport { ConsoleInstrumentation } from '../instrumentations/console';\nimport { CrashReportingInstrumentation } from '../instrumentations/crashReporting';\nimport { ErrorsInstrumentation } from '../instrumentations/errors';\nimport { FrameMonitoringInstrumentation } from '../instrumentations/frameMonitoring';\nimport { HttpInstrumentation } from '../instrumentations/http';\nimport { PerformanceInstrumentation } from '../instrumentations/performance';\nimport { SessionInstrumentation } from '../instrumentations/session';\nimport { StartupInstrumentation } from '../instrumentations/startup';\nimport { UserActionInstrumentation } from '../instrumentations/userActions';\nimport { ViewInstrumentation } from '../instrumentations/view';\nimport { XHRInstrumentation } from '../instrumentations/xhr';\n\nimport type { ReactNativeConfig } from './types';\n\n/** Convert Patterns (string | RegExp)[] to RegExp[] for instrumentations that require RegExp[]. */\nfunction toRegExpArray(patterns: Patterns): RegExp[] {\n return patterns.map((p) => (typeof p === 'string' ? new RegExp(p.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')) : p));\n}\n\n/**\n * Returns the default set of instrumentations for React Native.\n *\n * Reads all options from the Faro config (single source of truth).\n * Property names are aligned with Flutter SDK FaroConfig:\n * - cpuUsageVitals, memoryUsageVitals, anrTracking, refreshRateVitals\n * - enableCrashReporting, enableErrorReporting\n * - fetchVitalsInterval, ignoreUrls\n *\n * @example\n * ```ts\n * const config: ReactNativeConfig = {\n * app: { name: 'my-app', version: '1.0.0' },\n * cpuUsageVitals: true,\n * memoryUsageVitals: true,\n * anrTracking: true,\n * refreshRateVitals: true,\n * enableCrashReporting: true,\n * fetchVitalsInterval: 30000,\n * instrumentations: getRNInstrumentations(config),\n * };\n * ```\n */\nexport function getRNInstrumentations(config: Partial<ReactNativeConfig> = {}): Instrumentation[] {\n // Aligned with Flutter SDK FaroConfig defaults\n const {\n // Error & crash tracking\n enableErrorReporting = true,\n enableCrashReporting = false,\n anrTracking = false,\n anrOptions = {},\n\n // Performance vitals\n cpuUsageVitals = true,\n memoryUsageVitals = true,\n refreshRateVitals = false,\n fetchVitalsInterval = 30000,\n frameMonitoringOptions = {},\n\n // Network\n ignoreUrls = [],\n enableHttpInstrumentation = {},\n\n // Console and user actions\n enableConsoleCapture = true,\n enableUserActions = true,\n\n // Tracing\n enableTracing = false,\n tracingOptions = {},\n } = config;\n\n const instrumentations: Instrumentation[] = [];\n\n // Error reporting (Flutter: enableFlutterErrorReporting)\n if (enableErrorReporting) {\n instrumentations.push(new ErrorsInstrumentation());\n }\n\n // Console capture - not in Flutter SDK, RN-specific\n if (enableConsoleCapture) {\n instrumentations.push(new ConsoleInstrumentation());\n }\n\n // Sessions - always enabled in Flutter, same here\n instrumentations.push(new SessionInstrumentation());\n\n // Views - always enabled in Flutter, same here\n instrumentations.push(new ViewInstrumentation());\n\n // App state - always enabled in Flutter, same here\n instrumentations.push(new AppStateInstrumentation());\n\n // User actions - enabled by default, opt-out via enableUserActions\n if (enableUserActions) {\n instrumentations.push(new UserActionInstrumentation());\n }\n\n // HTTP/XHR tracking: when tracing is enabled, TracingInstrumentation patches fetch and XHR.\n // When tracing is disabled, add HttpInstrumentation and/or XHRInstrumentation per enableHttpInstrumentation.\n if (!enableTracing) {\n const { fetch: enableFetch = true, xhr: enableXhr = true } = enableHttpInstrumentation;\n const ignoredUrlsRegExp = toRegExpArray(ignoreUrls);\n if (enableFetch) {\n instrumentations.push(new HttpInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n if (enableXhr) {\n instrumentations.push(new XHRInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n }\n\n // Performance vitals (CPU/memory) - only add if at least one metric is enabled\n if (cpuUsageVitals || memoryUsageVitals) {\n const perfInstrumentation = new PerformanceInstrumentation({\n memoryUsageVitals,\n cpuUsageVitals,\n fetchVitalsInterval,\n });\n instrumentations.push(perfInstrumentation);\n }\n\n // Startup tracking - always enabled\n instrumentations.push(new StartupInstrumentation());\n\n // Frame monitoring: enabled when refreshRateVitals is true\n // The instrumentation reads refreshRateVitals from faro.config (single source of truth)\n if (refreshRateVitals) {\n instrumentations.push(new FrameMonitoringInstrumentation(frameMonitoringOptions));\n }\n\n // ANR detection (Android only)\n if (anrTracking && Platform.OS === 'android') {\n instrumentations.push(new ANRInstrumentation(anrOptions));\n }\n\n // Crash reporting\n if (enableCrashReporting) {\n instrumentations.push(new CrashReportingInstrumentation());\n }\n\n if (enableTracing) {\n try {\n const { TracingInstrumentation } = require('@grafana/faro-react-native-tracing');\n instrumentations.push(new TracingInstrumentation(tracingOptions));\n } catch {\n console.warn(\n '[Faro] enableTracing is true but @grafana/faro-react-native-tracing is not installed. ' +\n 'Add it to use tracing: yarn add @grafana/faro-react-native-tracing'\n );\n }\n }\n\n return instrumentations;\n}\n"]}
1
+ {"version":3,"file":"getRNInstrumentations.js","sourceRoot":"","sources":["../../../src/config/getRNInstrumentations.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\n\nimport type { Instrumentation, Patterns } from '@grafana/faro-core';\n\nimport { ANRInstrumentation } from '../instrumentations/anr';\nimport { AppStateInstrumentation } from '../instrumentations/appState';\nimport { ConsoleInstrumentation } from '../instrumentations/console';\nimport { CrashReportingInstrumentation } from '../instrumentations/crashReporting';\nimport { ErrorsInstrumentation } from '../instrumentations/errors';\nimport { FrameMonitoringInstrumentation } from '../instrumentations/frameMonitoring';\nimport { HttpInstrumentation } from '../instrumentations/http';\nimport { PerformanceInstrumentation } from '../instrumentations/performance';\nimport { SessionInstrumentation } from '../instrumentations/session';\nimport { StartupInstrumentation } from '../instrumentations/startup';\nimport { UserActionInstrumentation } from '../instrumentations/userActions';\nimport { ViewInstrumentation } from '../instrumentations/view';\nimport { XHRInstrumentation } from '../instrumentations/xhr';\n\nimport type { ReactNativeConfig } from './types';\n\n/** Convert Patterns (string | RegExp)[] to RegExp[] for instrumentations that require RegExp[]. */\nfunction toRegExpArray(patterns: Patterns): RegExp[] {\n return patterns.map((p) => (typeof p === 'string' ? new RegExp(p.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')) : p));\n}\n\n/**\n * Returns the default set of instrumentations for React Native.\n *\n * Reads all options from the Faro config (single source of truth).\n * Property names are aligned with Flutter SDK FaroConfig:\n * - cpuUsageVitals, memoryUsageVitals, anrTracking, refreshRateVitals\n * - enableCrashReporting, enableErrorReporting\n * - fetchVitalsInterval, ignoreUrls\n *\n * @example\n * ```ts\n * const config: ReactNativeConfig = {\n * app: { name: 'my-app', version: '1.0.0' },\n * cpuUsageVitals: true,\n * memoryUsageVitals: true,\n * anrTracking: true,\n * refreshRateVitals: true,\n * enableCrashReporting: true,\n * fetchVitalsInterval: 30000,\n * instrumentations: getRNInstrumentations(config),\n * };\n * ```\n */\nexport function getRNInstrumentations(config: Partial<ReactNativeConfig> = {}): Instrumentation[] {\n // Aligned with Flutter SDK FaroConfig defaults\n const {\n // Error & crash tracking\n enableErrorReporting = true,\n enableCrashReporting = false,\n anrTracking = false,\n anrOptions = {},\n\n // Performance vitals\n cpuUsageVitals = true,\n memoryUsageVitals = true,\n refreshRateVitals = false,\n fetchVitalsInterval = 30000,\n frameMonitoringOptions = {},\n\n // Network\n ignoreUrls = [],\n enableHttpInstrumentation = {},\n\n // Console and user actions\n enableConsoleCapture = true,\n enableUserActions = true,\n\n // Tracing\n enableTracing = false,\n tracingOptions = {},\n } = config;\n\n const instrumentations: Instrumentation[] = [];\n\n // Error reporting (Flutter: enableFlutterErrorReporting)\n if (enableErrorReporting) {\n instrumentations.push(\n new ErrorsInstrumentation({\n releaseBundleFilename: config.releaseBundleFilename,\n })\n );\n }\n\n // Console capture - not in Flutter SDK, RN-specific\n if (enableConsoleCapture) {\n instrumentations.push(new ConsoleInstrumentation());\n }\n\n // Sessions - always enabled in Flutter, same here\n instrumentations.push(new SessionInstrumentation());\n\n // Views - always enabled in Flutter, same here\n instrumentations.push(new ViewInstrumentation());\n\n // App state - always enabled in Flutter, same here\n instrumentations.push(new AppStateInstrumentation());\n\n // User actions - enabled by default, opt-out via enableUserActions\n if (enableUserActions) {\n instrumentations.push(new UserActionInstrumentation());\n }\n\n // HTTP/XHR tracking: when tracing is enabled, TracingInstrumentation patches fetch and XHR.\n // When tracing is disabled, add HttpInstrumentation and/or XHRInstrumentation per enableHttpInstrumentation.\n if (!enableTracing) {\n const { fetch: enableFetch = true, xhr: enableXhr = true } = enableHttpInstrumentation;\n const ignoredUrlsRegExp = toRegExpArray(ignoreUrls);\n if (enableFetch) {\n instrumentations.push(new HttpInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n if (enableXhr) {\n instrumentations.push(new XHRInstrumentation({ ignoredUrls: ignoredUrlsRegExp }));\n }\n }\n\n // Performance vitals (CPU/memory) - only add if at least one metric is enabled\n if (cpuUsageVitals || memoryUsageVitals) {\n const perfInstrumentation = new PerformanceInstrumentation({\n memoryUsageVitals,\n cpuUsageVitals,\n fetchVitalsInterval,\n });\n instrumentations.push(perfInstrumentation);\n }\n\n // Startup tracking - always enabled\n instrumentations.push(new StartupInstrumentation());\n\n // Frame monitoring: enabled when refreshRateVitals is true\n // The instrumentation reads refreshRateVitals from faro.config (single source of truth)\n if (refreshRateVitals) {\n instrumentations.push(new FrameMonitoringInstrumentation(frameMonitoringOptions));\n }\n\n // ANR detection (Android only)\n if (anrTracking && Platform.OS === 'android') {\n instrumentations.push(new ANRInstrumentation(anrOptions));\n }\n\n // Crash reporting\n if (enableCrashReporting) {\n instrumentations.push(new CrashReportingInstrumentation());\n }\n\n if (enableTracing) {\n try {\n const { TracingInstrumentation } = require('@grafana/faro-react-native-tracing');\n instrumentations.push(new TracingInstrumentation(tracingOptions));\n } catch {\n console.warn(\n '[Faro] enableTracing is true but @grafana/faro-react-native-tracing is not installed. ' +\n 'Add it to use tracing: yarn add @grafana/faro-react-native-tracing'\n );\n }\n }\n\n return instrumentations;\n}\n"]}
@@ -71,9 +71,11 @@ function buildInstrumentations(config) {
71
71
  /**
72
72
  * React Native stacktrace parser. Uses getStackFramesFromError for RN-specific formats.
73
73
  */
74
- var parseStacktrace = function (err) { return ({
75
- frames: (0, stackTraceParser_1.getStackFramesFromError)(err),
76
- }); };
74
+ function createParseStacktrace(releaseBundleFilename) {
75
+ return function (err) { return ({
76
+ frames: (0, stackTraceParser_1.getStackFramesFromError)(err, { releaseBundleFilename: releaseBundleFilename }),
77
+ }); };
78
+ }
77
79
  /**
78
80
  * Creates a full Faro config from React Native flag-based config.
79
81
  *
@@ -88,9 +90,10 @@ function makeRNConfig(config, preloadedSessionDeviceAttributes) {
88
90
  var customMetas = (_a = config.metas) !== null && _a !== void 0 ? _a : [];
89
91
  var transports = buildTransports(config);
90
92
  var instrumentations = buildInstrumentations(config);
93
+ var releaseBundleFilename = config.releaseBundleFilename;
91
94
  return __assign(__assign({ app: config.app }, (preloadedSessionDeviceAttributes != null && {
92
95
  preloadedSessionDeviceAttributes: preloadedSessionDeviceAttributes,
93
- })), { dedupe: (_b = config.dedupe) !== null && _b !== void 0 ? _b : true, globalObjectKey: (_c = config.globalObjectKey) !== null && _c !== void 0 ? _c : faro_core_1.defaultGlobalObjectKey, internalLoggerLevel: (_d = config.internalLoggerLevel) !== null && _d !== void 0 ? _d : internalLogger_1.InternalLoggerLevel.ERROR, isolate: (_e = config.isolate) !== null && _e !== void 0 ? _e : false, parseStacktrace: (_f = config.parseStacktrace) !== null && _f !== void 0 ? _f : parseStacktrace, paused: (_g = config.paused) !== null && _g !== void 0 ? _g : false, preventGlobalExposure: (_h = config.preventGlobalExposure) !== null && _h !== void 0 ? _h : false, unpatchedConsole: (_j = config.unpatchedConsole) !== null && _j !== void 0 ? _j : faro_core_1.defaultUnpatchedConsole, batching: {
96
+ })), { dedupe: (_b = config.dedupe) !== null && _b !== void 0 ? _b : true, globalObjectKey: (_c = config.globalObjectKey) !== null && _c !== void 0 ? _c : faro_core_1.defaultGlobalObjectKey, internalLoggerLevel: (_d = config.internalLoggerLevel) !== null && _d !== void 0 ? _d : internalLogger_1.InternalLoggerLevel.ERROR, isolate: (_e = config.isolate) !== null && _e !== void 0 ? _e : false, parseStacktrace: (_f = config.parseStacktrace) !== null && _f !== void 0 ? _f : createParseStacktrace(releaseBundleFilename), paused: (_g = config.paused) !== null && _g !== void 0 ? _g : false, preventGlobalExposure: (_h = config.preventGlobalExposure) !== null && _h !== void 0 ? _h : false, unpatchedConsole: (_j = config.unpatchedConsole) !== null && _j !== void 0 ? _j : faro_core_1.defaultUnpatchedConsole, batching: {
94
97
  enabled: false,
95
98
  sendTimeout: 250,
96
99
  itemLimit: 50,
@@ -1 +1 @@
1
- {"version":3,"file":"makeRNConfig.js","sourceRoot":"","sources":["../../../src/config/makeRNConfig.ts"],"names":[],"mappings":"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","sourcesContent":["import { defaultGlobalObjectKey, defaultUnpatchedConsole } from '@grafana/faro-core';\nimport type { Config } from '@grafana/faro-core';\n\nimport { getStackFramesFromError } from '../instrumentations/errors/stackTraceParser';\nimport type { SessionAttributes } from '../instrumentations/session/sessionAttributes';\nimport { defaultSessionTrackingConfig } from '../instrumentations/session/sessionManager/sessionConstants';\nimport { InternalLoggerLevel, LogLevel } from '../internalLogger';\nimport { getPageMeta } from '../metas/page';\nimport { getScreenMeta } from '../metas/screen';\nimport { getSdkMeta } from '../metas/sdk';\nimport { ConsoleTransport } from '../transports/console';\nimport { FetchTransport } from '../transports/fetch';\nimport { OfflineTransport } from '../transports/offline';\n\nimport { getRNInstrumentations } from './getRNInstrumentations';\nimport type { ReactNativeConfig, ReactNativeFullConfig } from './types';\n\nconst DEFAULT_OFFLINE_CACHE_MS = 3 * 24 * 60 * 60 * 1000; // 3 days\n\n/**\n * Builds transports. FetchTransport is always added when url is provided.\n * User can enable offline and console via enableTransports.\n */\nfunction buildTransports(config: ReactNativeConfig): Config['transports'] {\n const { enableTransports = { offline: false, console: false } } = config;\n\n if (!config.url) {\n throw new Error('url is required. Provide the Faro collector URL.');\n }\n\n const builtTransports: Config['transports'] = [];\n\n if (enableTransports.offline) {\n builtTransports.push(\n new OfflineTransport({\n maxCacheDurationMs: DEFAULT_OFFLINE_CACHE_MS,\n })\n );\n }\n\n builtTransports.push(\n new FetchTransport({\n url: config.url,\n apiKey: config.apiKey,\n })\n );\n\n if (enableTransports.console) {\n builtTransports.push(new ConsoleTransport({ level: LogLevel.DEBUG }));\n }\n\n const extraTransports = config.transports ?? [];\n return [...builtTransports, ...extraTransports];\n}\n\n/**\n * Builds instrumentations from config flags.\n */\nfunction buildInstrumentations(config: ReactNativeConfig): Config['instrumentations'] {\n const baseInstrumentations = getRNInstrumentations(config);\n const extraInstrumentations = config.instrumentations ?? [];\n return [...baseInstrumentations, ...extraInstrumentations];\n}\n\n/**\n * React Native stacktrace parser. Uses getStackFramesFromError for RN-specific formats.\n */\nconst parseStacktrace: Config['parseStacktrace'] = (err) => ({\n frames: getStackFramesFromError(err),\n});\n\n/**\n * Creates a full Faro config from React Native flag-based config.\n *\n * Based on flags, builds instrumentations and transports automatically.\n * Client just enables what they need; makeRNConfig does the rest.\n *\n * @param preloadedSessionDeviceAttributes Device/session fields for session meta (passed from async `initializeFaro`).\n */\nexport function makeRNConfig(\n config: ReactNativeConfig,\n preloadedSessionDeviceAttributes?: SessionAttributes\n): ReactNativeFullConfig {\n const defaultMetas = [getSdkMeta(), getPageMeta(), getScreenMeta()];\n const customMetas = config.metas ?? [];\n const transports = buildTransports(config);\n const instrumentations = buildInstrumentations(config);\n\n return {\n app: config.app,\n ...(preloadedSessionDeviceAttributes != null && {\n preloadedSessionDeviceAttributes,\n }),\n dedupe: config.dedupe ?? true,\n globalObjectKey: config.globalObjectKey ?? defaultGlobalObjectKey,\n internalLoggerLevel: config.internalLoggerLevel ?? InternalLoggerLevel.ERROR,\n isolate: config.isolate ?? false,\n parseStacktrace: config.parseStacktrace ?? parseStacktrace,\n paused: config.paused ?? false,\n preventGlobalExposure: config.preventGlobalExposure ?? false,\n unpatchedConsole: config.unpatchedConsole ?? defaultUnpatchedConsole,\n batching: {\n enabled: false,\n sendTimeout: 250,\n itemLimit: 50,\n },\n sessionTracking: {\n ...defaultSessionTrackingConfig,\n ...config.sessionTracking,\n },\n metas: [...defaultMetas, ...customMetas],\n instrumentations,\n transports,\n ignoreUrls: config.ignoreUrls ?? [],\n ignoreErrors: config.ignoreErrors,\n beforeSend: config.beforeSend,\n preserveOriginalError: config.preserveOriginalError,\n userActionsInstrumentation: config.userActionsOptions,\n consoleInstrumentation: config.consoleCaptureOptions,\n };\n}\n"]}
1
+ {"version":3,"file":"makeRNConfig.js","sourceRoot":"","sources":["../../../src/config/makeRNConfig.ts"],"names":[],"mappings":"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","sourcesContent":["import { defaultGlobalObjectKey, defaultUnpatchedConsole } from '@grafana/faro-core';\nimport type { Config } from '@grafana/faro-core';\n\nimport { getStackFramesFromError } from '../instrumentations/errors/stackTraceParser';\nimport type { SessionAttributes } from '../instrumentations/session/sessionAttributes';\nimport { defaultSessionTrackingConfig } from '../instrumentations/session/sessionManager/sessionConstants';\nimport { InternalLoggerLevel, LogLevel } from '../internalLogger';\nimport { getPageMeta } from '../metas/page';\nimport { getScreenMeta } from '../metas/screen';\nimport { getSdkMeta } from '../metas/sdk';\nimport { ConsoleTransport } from '../transports/console';\nimport { FetchTransport } from '../transports/fetch';\nimport { OfflineTransport } from '../transports/offline';\n\nimport { getRNInstrumentations } from './getRNInstrumentations';\nimport type { ReactNativeConfig, ReactNativeFullConfig } from './types';\n\nconst DEFAULT_OFFLINE_CACHE_MS = 3 * 24 * 60 * 60 * 1000; // 3 days\n\n/**\n * Builds transports. FetchTransport is always added when url is provided.\n * User can enable offline and console via enableTransports.\n */\nfunction buildTransports(config: ReactNativeConfig): Config['transports'] {\n const { enableTransports = { offline: false, console: false } } = config;\n\n if (!config.url) {\n throw new Error('url is required. Provide the Faro collector URL.');\n }\n\n const builtTransports: Config['transports'] = [];\n\n if (enableTransports.offline) {\n builtTransports.push(\n new OfflineTransport({\n maxCacheDurationMs: DEFAULT_OFFLINE_CACHE_MS,\n })\n );\n }\n\n builtTransports.push(\n new FetchTransport({\n url: config.url,\n apiKey: config.apiKey,\n })\n );\n\n if (enableTransports.console) {\n builtTransports.push(new ConsoleTransport({ level: LogLevel.DEBUG }));\n }\n\n const extraTransports = config.transports ?? [];\n return [...builtTransports, ...extraTransports];\n}\n\n/**\n * Builds instrumentations from config flags.\n */\nfunction buildInstrumentations(config: ReactNativeConfig): Config['instrumentations'] {\n const baseInstrumentations = getRNInstrumentations(config);\n const extraInstrumentations = config.instrumentations ?? [];\n return [...baseInstrumentations, ...extraInstrumentations];\n}\n\n/**\n * React Native stacktrace parser. Uses getStackFramesFromError for RN-specific formats.\n */\nfunction createParseStacktrace(releaseBundleFilename: string | undefined): Config['parseStacktrace'] {\n return (err) => ({\n frames: getStackFramesFromError(err, { releaseBundleFilename }),\n });\n}\n\n/**\n * Creates a full Faro config from React Native flag-based config.\n *\n * Based on flags, builds instrumentations and transports automatically.\n * Client just enables what they need; makeRNConfig does the rest.\n *\n * @param preloadedSessionDeviceAttributes Device/session fields for session meta (passed from async `initializeFaro`).\n */\nexport function makeRNConfig(\n config: ReactNativeConfig,\n preloadedSessionDeviceAttributes?: SessionAttributes\n): ReactNativeFullConfig {\n const defaultMetas = [getSdkMeta(), getPageMeta(), getScreenMeta()];\n const customMetas = config.metas ?? [];\n const transports = buildTransports(config);\n const instrumentations = buildInstrumentations(config);\n\n const releaseBundleFilename = config.releaseBundleFilename;\n return {\n app: config.app,\n ...(preloadedSessionDeviceAttributes != null && {\n preloadedSessionDeviceAttributes,\n }),\n dedupe: config.dedupe ?? true,\n globalObjectKey: config.globalObjectKey ?? defaultGlobalObjectKey,\n internalLoggerLevel: config.internalLoggerLevel ?? InternalLoggerLevel.ERROR,\n isolate: config.isolate ?? false,\n parseStacktrace: config.parseStacktrace ?? createParseStacktrace(releaseBundleFilename),\n paused: config.paused ?? false,\n preventGlobalExposure: config.preventGlobalExposure ?? false,\n unpatchedConsole: config.unpatchedConsole ?? defaultUnpatchedConsole,\n batching: {\n enabled: false,\n sendTimeout: 250,\n itemLimit: 50,\n },\n sessionTracking: {\n ...defaultSessionTrackingConfig,\n ...config.sessionTracking,\n },\n metas: [...defaultMetas, ...customMetas],\n instrumentations,\n transports,\n ignoreUrls: config.ignoreUrls ?? [],\n ignoreErrors: config.ignoreErrors,\n beforeSend: config.beforeSend,\n preserveOriginalError: config.preserveOriginalError,\n userActionsInstrumentation: config.userActionsOptions,\n consoleInstrumentation: config.consoleCaptureOptions,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Config } from '@grafana/faro-core';\n\nimport type { ANRInstrumentationOptions } from '../instrumentations/anr';\nimport type { FrameMonitoringOptions } from '../instrumentations/frameMonitoring';\nimport type { SessionAttributes } from '../instrumentations/session/sessionAttributes';\n\nimport type { Sampling } from './sampling';\n\n/**\n * React Native session tracking config.\n * Extends Config['sessionTracking'] with sampling (Flutter-style) and RN-specific props.\n * Excludes samplingRate and sampler in favor of sampling.\n *\n * When `persistent` is true, sessions are stored with [react-native-mmkv](https://github.com/mrousavy/react-native-mmkv)\n * (synchronous). Add `react-native-mmkv` to your app and rebuild native projects.\n */\nexport type ReactNativeSessionTrackingConfig = Omit<\n NonNullable<Config['sessionTracking']>,\n 'samplingRate' | 'sampler'\n> & {\n /**\n * Session sampling. Use {@link SamplingRate} for fixed rate or\n * {@link SamplingFunction} for dynamic sampling.\n */\n sampling?: Sampling;\n};\n\n/**\n * Flags for enabling optional built-in transports.\n * FetchTransport is always instantiated when url is provided.\n */\nexport interface EnableTransportsConfig {\n /** Enable OfflineTransport for caching when offline (default: false) */\n offline?: boolean;\n /** Enable ConsoleTransport to log telemetry to Metro console for debugging (default: false) */\n console?: boolean;\n}\n\n/** RN-specific config: overrides + new props. Omit faro-core props we replace with RN equivalents. */\ntype ReactNativeConfigOverrides = {\n /** Application metadata (required) */\n app: Config['app'];\n sessionTracking?: ReactNativeSessionTrackingConfig;\n url?: string;\n apiKey?: string;\n enableTransports?: EnableTransportsConfig;\n /**\n * Which HTTP instrumentations to enable. When tracing is disabled, these control\n * HttpInstrumentation (fetch) and XHRInstrumentation (xhr). Default: both true.\n */\n enableHttpInstrumentation?: {\n /** HttpInstrumentation - patches fetch API (default: true) */\n fetch?: boolean;\n /** XHRInstrumentation - patches XMLHttpRequest (default: true) */\n xhr?: boolean;\n };\n enableTracing?: boolean;\n /** Passed to TracingInstrumentation. See TracingInstrumentationOptions in @grafana/faro-react-native-tracing. */\n tracingOptions?: Record<string, unknown>;\n cpuUsageVitals?: boolean;\n memoryUsageVitals?: boolean;\n refreshRateVitals?: boolean;\n fetchVitalsInterval?: number;\n frameMonitoringOptions?: FrameMonitoringOptions;\n enableErrorReporting?: boolean;\n enableCrashReporting?: boolean;\n anrTracking?: boolean;\n anrOptions?: ANRInstrumentationOptions;\n enableConsoleCapture?: boolean;\n consoleCaptureOptions?: Config['consoleInstrumentation'];\n enableUserActions?: boolean;\n userActionsOptions?: Config['userActionsInstrumentation'];\n};\n\n/**\n * React Native config: Config (omit replaced props) & overrides.\n * Omit: sessionTracking, consoleInstrumentation, userActionsInstrumentation.\n * instrumentations and transports inherited from Config for extras (built-ins come from flags).\n */\nexport type ReactNativeConfig = Partial<\n Omit<Config, 'sessionTracking' | 'consoleInstrumentation' | 'userActionsInstrumentation'>\n> &\n ReactNativeConfigOverrides;\n\n/**\n * Internal full config returned by `makeRNConfig`.\n * Extends faro-core `Config` with RN-specific fields that instrumentations need at init time.\n */\nexport type ReactNativeFullConfig = Config & {\n preloadedSessionDeviceAttributes?: SessionAttributes;\n};\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/config/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Config } from '@grafana/faro-core';\n\nimport type { ANRInstrumentationOptions } from '../instrumentations/anr';\nimport type { FrameMonitoringOptions } from '../instrumentations/frameMonitoring';\nimport type { SessionAttributes } from '../instrumentations/session/sessionAttributes';\n\nimport type { Sampling } from './sampling';\n\n/**\n * React Native session tracking config.\n * Extends Config['sessionTracking'] with sampling (Flutter-style) and RN-specific props.\n * Excludes samplingRate and sampler in favor of sampling.\n *\n * When `persistent` is true, sessions are stored with [react-native-mmkv](https://github.com/mrousavy/react-native-mmkv)\n * (synchronous). Add `react-native-mmkv` to your app and rebuild native projects.\n */\nexport type ReactNativeSessionTrackingConfig = Omit<\n NonNullable<Config['sessionTracking']>,\n 'samplingRate' | 'sampler'\n> & {\n /**\n * Session sampling. Use {@link SamplingRate} for fixed rate or\n * {@link SamplingFunction} for dynamic sampling.\n */\n sampling?: Sampling;\n};\n\n/**\n * Flags for enabling optional built-in transports.\n * FetchTransport is always instantiated when url is provided.\n */\nexport interface EnableTransportsConfig {\n /** Enable OfflineTransport for caching when offline (default: false) */\n offline?: boolean;\n /** Enable ConsoleTransport to log telemetry to Metro console for debugging (default: false) */\n console?: boolean;\n}\n\n/** RN-specific config: overrides + new props. Omit faro-core props we replace with RN equivalents. */\ntype ReactNativeConfigOverrides = {\n /** Application metadata (required) */\n app: Config['app'];\n sessionTracking?: ReactNativeSessionTrackingConfig;\n url?: string;\n apiKey?: string;\n enableTransports?: EnableTransportsConfig;\n /**\n * Which HTTP instrumentations to enable. When tracing is disabled, these control\n * HttpInstrumentation (fetch) and XHRInstrumentation (xhr). Default: both true.\n */\n enableHttpInstrumentation?: {\n /** HttpInstrumentation - patches fetch API (default: true) */\n fetch?: boolean;\n /** XHRInstrumentation - patches XMLHttpRequest (default: true) */\n xhr?: boolean;\n };\n enableTracing?: boolean;\n /** Passed to TracingInstrumentation. See TracingInstrumentationOptions in @grafana/faro-react-native-tracing. */\n tracingOptions?: Record<string, unknown>;\n cpuUsageVitals?: boolean;\n memoryUsageVitals?: boolean;\n refreshRateVitals?: boolean;\n fetchVitalsInterval?: number;\n frameMonitoringOptions?: FrameMonitoringOptions;\n enableErrorReporting?: boolean;\n enableCrashReporting?: boolean;\n anrTracking?: boolean;\n anrOptions?: ANRInstrumentationOptions;\n enableConsoleCapture?: boolean;\n consoleCaptureOptions?: Config['consoleInstrumentation'];\n enableUserActions?: boolean;\n userActionsOptions?: Config['userActionsInstrumentation'];\n /**\n * Hermes / minified release stack lines omit a file path. This must match the Metro plugin\n * source map top-level `file` (default `bundle.js`, or your `sourceMapFile` option there).\n */\n releaseBundleFilename?: string;\n};\n\n/**\n * React Native config: Config (omit replaced props) & overrides.\n * Omit: sessionTracking, consoleInstrumentation, userActionsInstrumentation.\n * instrumentations and transports inherited from Config for extras (built-ins come from flags).\n */\nexport type ReactNativeConfig = Partial<\n Omit<Config, 'sessionTracking' | 'consoleInstrumentation' | 'userActionsInstrumentation'>\n> &\n ReactNativeConfigOverrides;\n\n/**\n * Internal full config returned by `makeRNConfig`.\n * Extends faro-core `Config` with RN-specific fields that instrumentations need at init time.\n */\nexport type ReactNativeFullConfig = Config & {\n preloadedSessionDeviceAttributes?: SessionAttributes;\n};\n"]}
@@ -5,5 +5,5 @@ exports.FARO_REACT_NATIVE_NPM_VERSION = exports.FARO_REACT_NATIVE_NPM_NAME = voi
5
5
  * NPM package identity for this build. Generated by scripts/sync-faro-rn-package-meta.mjs - do not edit.
6
6
  */
7
7
  exports.FARO_REACT_NATIVE_NPM_NAME = '@grafana/faro-react-native';
8
- exports.FARO_REACT_NATIVE_NPM_VERSION = '1.0.0';
8
+ exports.FARO_REACT_NATIVE_NPM_VERSION = '1.2.0';
9
9
  //# sourceMappingURL=faroRNPackageMeta.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"faroRNPackageMeta.js","sourceRoot":"","sources":["../../../src/generated/faroRNPackageMeta.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,0BAA0B,GAAG,4BAAqC,CAAC;AACnE,QAAA,6BAA6B,GAAG,OAAgB,CAAC","sourcesContent":["/**\n * NPM package identity for this build. Generated by scripts/sync-faro-rn-package-meta.mjs - do not edit.\n */\nexport const FARO_REACT_NATIVE_NPM_NAME = '@grafana/faro-react-native' as const;\nexport const FARO_REACT_NATIVE_NPM_VERSION = '1.0.0' as const;\n"]}
1
+ {"version":3,"file":"faroRNPackageMeta.js","sourceRoot":"","sources":["../../../src/generated/faroRNPackageMeta.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,0BAA0B,GAAG,4BAAqC,CAAC;AACnE,QAAA,6BAA6B,GAAG,OAAgB,CAAC","sourcesContent":["/**\n * NPM package identity for this build. Generated by scripts/sync-faro-rn-package-meta.mjs - do not edit.\n */\nexport const FARO_REACT_NATIVE_NPM_NAME = '@grafana/faro-react-native' as const;\nexport const FARO_REACT_NATIVE_NPM_VERSION = '1.2.0' as const;\n"]}
@@ -61,6 +61,7 @@ var ErrorsInstrumentation = /** @class */ (function (_super) {
61
61
  enableDeduplication: (_b = options.enableDeduplication) !== null && _b !== void 0 ? _b : true,
62
62
  deduplicationWindow: (_c = options.deduplicationWindow) !== null && _c !== void 0 ? _c : 5000,
63
63
  maxDeduplicationEntries: (_d = options.maxDeduplicationEntries) !== null && _d !== void 0 ? _d : 50,
64
+ releaseBundleFilename: options.releaseBundleFilename,
64
65
  };
65
66
  return _this;
66
67
  }
@@ -89,7 +90,7 @@ var ErrorsInstrumentation = /** @class */ (function (_super) {
89
90
  var _a = (0, stackTraceParser_1.enhanceErrorWithContext)(error, {
90
91
  mechanism: const_1.ErrorMechanism.UNCAUGHT,
91
92
  isFatal: String(isFatal !== null && isFatal !== void 0 ? isFatal : false),
92
- }), enhancedError = _a.error, stackFrames = _a.stackFrames, context = _a.context;
93
+ }, { releaseBundleFilename: _this.options.releaseBundleFilename }), enhancedError = _a.error, stackFrames = _a.stackFrames, context = _a.context;
93
94
  // Push error to Faro with enhanced data (type from error.name, matches Web SDK)
94
95
  _this.api.pushError(enhancedError, {
95
96
  type: enhancedError.name || 'Error',
@@ -139,7 +140,7 @@ var ErrorsInstrumentation = /** @class */ (function (_super) {
139
140
  // Enhance error with React Native context and stack frames
140
141
  var _a = (0, stackTraceParser_1.enhanceErrorWithContext)(error, {
141
142
  mechanism: const_1.ErrorMechanism.UNHANDLED_REJECTION,
142
- }), enhancedError = _a.error, stackFrames = _a.stackFrames, context = _a.context;
143
+ }, { releaseBundleFilename: _this.options.releaseBundleFilename }), enhancedError = _a.error, stackFrames = _a.stackFrames, context = _a.context;
143
144
  // Type: use actual error type for Error objects; 'UnhandledRejection' for primitives (Web SDK)
144
145
  var errorType = isPrimitiveRejection ? const_1.primitiveUnhandledType : enhancedError.name || 'Error';
145
146
  _this.api.pushError(enhancedError, {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/instrumentations/errors/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ErrorUtils } from 'react-native';\n\nimport { BaseInstrumentation } from '@grafana/faro-core';\n\nimport { ErrorMechanism, primitiveUnhandledType } from './const';\nexport { ErrorMechanism } from './const';\nexport type { ErrorMechanismType } from './const';\nimport { enhanceErrorWithContext } from './stackTraceParser';\n\n// Access the global ErrorUtils\ndeclare const global: {\n ErrorUtils: ErrorUtils;\n addEventListener?: (event: string, handler: (event: PromiseRejectionEvent) => void) => void;\n removeEventListener?: (event: string, handler: (event: PromiseRejectionEvent) => void) => void;\n};\n\ntype ErrorHandlerCallback = (error: Error | unknown, isFatal?: boolean) => void;\n\nexport interface ErrorsInstrumentationOptions {\n /**\n * Patterns to ignore errors by message\n * @example [/network timeout/i, /cancelled/i]\n */\n ignoreErrors?: RegExp[];\n\n /**\n * Enable error deduplication (default: true)\n * Prevents sending the same error multiple times within a time window\n */\n enableDeduplication?: boolean;\n\n /**\n * Deduplication time window in milliseconds (default: 5000)\n * Errors with same message/stack within this window are considered duplicates\n */\n deduplicationWindow?: number;\n\n /**\n * Maximum number of errors to track for deduplication (default: 50)\n * Older errors are removed when this limit is reached\n */\n maxDeduplicationEntries?: number;\n}\n\ninterface ErrorFingerprint {\n message: string;\n stack: string;\n timestamp: number;\n}\n\n/**\n * Errors instrumentation for React Native\n *\n * Features:\n * - Captures unhandled errors and promise rejections using ErrorUtils\n * - Parses React Native stack traces (dev, release, Metro bundler formats)\n * - Adds platform context (OS, version, Hermes engine)\n * - Error deduplication to prevent duplicate reports\n * - Configurable error filtering\n *\n * @example\n * ```tsx\n * import { ErrorsInstrumentation, initializeFaro } from '@grafana/faro-react-native';\n *\n * await initializeFaro({\n * // ...config\n * instrumentations: [\n * new ErrorsInstrumentation({\n * ignoreErrors: [/network timeout/i, /cancelled/i],\n * enableDeduplication: true,\n * deduplicationWindow: 5000,\n * }),\n * ],\n * });\n * ```\n */\nexport class ErrorsInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-react-native-errors';\n readonly version = '1.0.0';\n\n private originalErrorHandler?: ErrorHandlerCallback;\n private unhandledRejectionListener?: (event: PromiseRejectionEvent) => void;\n private options: Required<ErrorsInstrumentationOptions>;\n private errorFingerprints: ErrorFingerprint[] = [];\n\n constructor(options: ErrorsInstrumentationOptions = {}) {\n super();\n this.options = {\n ignoreErrors: options.ignoreErrors ?? [],\n enableDeduplication: options.enableDeduplication ?? true,\n deduplicationWindow: options.deduplicationWindow ?? 5000,\n maxDeduplicationEntries: options.maxDeduplicationEntries ?? 50,\n };\n }\n\n initialize(): void {\n // Capture unhandled JavaScript errors\n this.setupGlobalErrorHandler();\n\n // Capture unhandled promise rejections\n this.setupUnhandledRejectionHandler();\n }\n\n private setupGlobalErrorHandler(): void {\n // Store the original error handler\n this.originalErrorHandler = global.ErrorUtils.getGlobalHandler();\n\n // Set our custom handler\n global.ErrorUtils.setGlobalHandler((error: Error, isFatal?: boolean) => {\n try {\n // Check if error should be ignored\n if (this.shouldIgnoreError(error)) {\n return;\n }\n\n // Check for duplicate errors\n if (this.options.enableDeduplication && this.isDuplicateError(error)) {\n return;\n }\n\n // Enhance error with React Native context and stack frames\n const {\n error: enhancedError,\n stackFrames,\n context,\n } = enhanceErrorWithContext(error, {\n mechanism: ErrorMechanism.UNCAUGHT,\n isFatal: String(isFatal ?? false),\n });\n\n // Push error to Faro with enhanced data (type from error.name, matches Web SDK)\n this.api.pushError(enhancedError, {\n type: enhancedError.name || 'Error',\n context,\n stackFrames,\n });\n\n // Track error fingerprint for deduplication\n if (this.options.enableDeduplication) {\n this.addErrorFingerprint(error);\n }\n } catch (_e) {\n // Don't let error reporting cause more errors\n } finally {\n // Always call the original handler to maintain normal error behavior\n if (this.originalErrorHandler) {\n this.originalErrorHandler(error, isFatal);\n }\n }\n });\n }\n\n private setupUnhandledRejectionHandler(): void {\n // React Native supports the standard unhandledrejection event\n this.unhandledRejectionListener = (event: PromiseRejectionEvent) => {\n try {\n const reason = event.reason;\n\n // Convert reason to an Error if it isn't one (matches Web SDK approach)\n const isPrimitiveRejection = !(reason instanceof Error);\n let error: Error;\n if (reason instanceof Error) {\n error = reason;\n } else {\n error = new Error(\n `Unhandled Promise Rejection: ${typeof reason === 'object' ? JSON.stringify(reason) : String(reason)}`\n );\n }\n\n // Check if error should be ignored\n if (this.shouldIgnoreError(error)) {\n return;\n }\n\n // Check for duplicate errors\n if (this.options.enableDeduplication && this.isDuplicateError(error)) {\n return;\n }\n\n // Enhance error with React Native context and stack frames\n const {\n error: enhancedError,\n stackFrames,\n context,\n } = enhanceErrorWithContext(error, {\n mechanism: ErrorMechanism.UNHANDLED_REJECTION,\n });\n\n // Type: use actual error type for Error objects; 'UnhandledRejection' for primitives (Web SDK)\n const errorType = isPrimitiveRejection ? primitiveUnhandledType : enhancedError.name || 'Error';\n\n this.api.pushError(enhancedError, {\n type: errorType,\n context,\n stackFrames,\n });\n\n // Track error fingerprint for deduplication\n if (this.options.enableDeduplication) {\n this.addErrorFingerprint(error);\n }\n } catch (_e) {\n // Don't let error reporting cause more errors\n }\n };\n\n // Add the listener\n global.addEventListener?.('unhandledrejection', this.unhandledRejectionListener);\n }\n\n /**\n * Check if error should be ignored based on ignoreErrors patterns\n */\n private shouldIgnoreError(error: Error): boolean {\n if (!error || !error.message) {\n return false;\n }\n\n return this.options.ignoreErrors.some((pattern) => pattern.test(error.message));\n }\n\n /**\n * Check if error is a duplicate based on message and stack\n */\n private isDuplicateError(error: Error): boolean {\n const message = error.message || '';\n const stack = error.stack || '';\n const now = Date.now();\n\n // Clean up old entries first\n this.cleanupOldFingerprints(now);\n\n // Check if we've seen this error recently\n return this.errorFingerprints.some((fingerprint) => {\n return (\n fingerprint.message === message &&\n fingerprint.stack === stack &&\n now - fingerprint.timestamp < this.options.deduplicationWindow\n );\n });\n }\n\n /**\n * Add error fingerprint for deduplication tracking\n */\n private addErrorFingerprint(error: Error): void {\n const message = error.message || '';\n const stack = error.stack || '';\n const timestamp = Date.now();\n\n this.errorFingerprints.push({ message, stack, timestamp });\n\n // Limit the number of tracked fingerprints\n if (this.errorFingerprints.length > this.options.maxDeduplicationEntries) {\n this.errorFingerprints.shift();\n }\n }\n\n /**\n * Remove error fingerprints older than the deduplication window\n */\n private cleanupOldFingerprints(now: number): void {\n this.errorFingerprints = this.errorFingerprints.filter(\n (fingerprint) => now - fingerprint.timestamp < this.options.deduplicationWindow\n );\n }\n\n unpatch(): void {\n // Restore original error handler\n if (this.originalErrorHandler) {\n global.ErrorUtils.setGlobalHandler(this.originalErrorHandler);\n }\n\n // Remove unhandled rejection listener\n if (this.unhandledRejectionListener) {\n global.removeEventListener?.('unhandledrejection', this.unhandledRejectionListener);\n }\n\n // Clear deduplication tracking\n this.errorFingerprints = [];\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/instrumentations/errors/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ErrorUtils } from 'react-native';\n\nimport { BaseInstrumentation } from '@grafana/faro-core';\n\nimport { ErrorMechanism, primitiveUnhandledType } from './const';\nexport { ErrorMechanism } from './const';\nexport type { ErrorMechanismType } from './const';\nimport { enhanceErrorWithContext } from './stackTraceParser';\n\n// Access the global ErrorUtils\ndeclare const global: {\n ErrorUtils: ErrorUtils;\n addEventListener?: (event: string, handler: (event: PromiseRejectionEvent) => void) => void;\n removeEventListener?: (event: string, handler: (event: PromiseRejectionEvent) => void) => void;\n};\n\ntype ErrorHandlerCallback = (error: Error | unknown, isFatal?: boolean) => void;\n\nexport interface ErrorsInstrumentationOptions {\n /**\n * Patterns to ignore errors by message\n * @example [/network timeout/i, /cancelled/i]\n */\n ignoreErrors?: RegExp[];\n\n /**\n * Enable error deduplication (default: true)\n * Prevents sending the same error multiple times within a time window\n */\n enableDeduplication?: boolean;\n\n /**\n * Deduplication time window in milliseconds (default: 5000)\n * Errors with same message/stack within this window are considered duplicates\n */\n deduplicationWindow?: number;\n\n /**\n * Maximum number of errors to track for deduplication (default: 50)\n * Older errors are removed when this limit is reached\n */\n maxDeduplicationEntries?: number;\n\n /**\n * Bundle filename for Hermes/minified release lines (`func@line:col`).\n * Must match the Metro plugin source map `file` field (default `bundle.js`).\n */\n releaseBundleFilename?: string;\n}\n\ninterface ErrorFingerprint {\n message: string;\n stack: string;\n timestamp: number;\n}\n\n/**\n * Errors instrumentation for React Native\n *\n * Features:\n * - Captures unhandled errors and promise rejections using ErrorUtils\n * - Parses React Native stack traces (dev, release, Metro bundler formats)\n * - Adds platform context (OS, version, Hermes engine)\n * - Error deduplication to prevent duplicate reports\n * - Configurable error filtering\n *\n * @example\n * ```tsx\n * import { ErrorsInstrumentation, initializeFaro } from '@grafana/faro-react-native';\n *\n * await initializeFaro({\n * // ...config\n * instrumentations: [\n * new ErrorsInstrumentation({\n * ignoreErrors: [/network timeout/i, /cancelled/i],\n * enableDeduplication: true,\n * deduplicationWindow: 5000,\n * }),\n * ],\n * });\n * ```\n */\nexport class ErrorsInstrumentation extends BaseInstrumentation {\n readonly name = '@grafana/faro-react-native-errors';\n readonly version = '1.0.0';\n\n private originalErrorHandler?: ErrorHandlerCallback;\n private unhandledRejectionListener?: (event: PromiseRejectionEvent) => void;\n private options: Required<Omit<ErrorsInstrumentationOptions, 'releaseBundleFilename'>> & {\n releaseBundleFilename?: string;\n };\n private errorFingerprints: ErrorFingerprint[] = [];\n\n constructor(options: ErrorsInstrumentationOptions = {}) {\n super();\n this.options = {\n ignoreErrors: options.ignoreErrors ?? [],\n enableDeduplication: options.enableDeduplication ?? true,\n deduplicationWindow: options.deduplicationWindow ?? 5000,\n maxDeduplicationEntries: options.maxDeduplicationEntries ?? 50,\n releaseBundleFilename: options.releaseBundleFilename,\n };\n }\n\n initialize(): void {\n // Capture unhandled JavaScript errors\n this.setupGlobalErrorHandler();\n\n // Capture unhandled promise rejections\n this.setupUnhandledRejectionHandler();\n }\n\n private setupGlobalErrorHandler(): void {\n // Store the original error handler\n this.originalErrorHandler = global.ErrorUtils.getGlobalHandler();\n\n // Set our custom handler\n global.ErrorUtils.setGlobalHandler((error: Error, isFatal?: boolean) => {\n try {\n // Check if error should be ignored\n if (this.shouldIgnoreError(error)) {\n return;\n }\n\n // Check for duplicate errors\n if (this.options.enableDeduplication && this.isDuplicateError(error)) {\n return;\n }\n\n // Enhance error with React Native context and stack frames\n const {\n error: enhancedError,\n stackFrames,\n context,\n } = enhanceErrorWithContext(\n error,\n {\n mechanism: ErrorMechanism.UNCAUGHT,\n isFatal: String(isFatal ?? false),\n },\n { releaseBundleFilename: this.options.releaseBundleFilename }\n );\n\n // Push error to Faro with enhanced data (type from error.name, matches Web SDK)\n this.api.pushError(enhancedError, {\n type: enhancedError.name || 'Error',\n context,\n stackFrames,\n });\n\n // Track error fingerprint for deduplication\n if (this.options.enableDeduplication) {\n this.addErrorFingerprint(error);\n }\n } catch (_e) {\n // Don't let error reporting cause more errors\n } finally {\n // Always call the original handler to maintain normal error behavior\n if (this.originalErrorHandler) {\n this.originalErrorHandler(error, isFatal);\n }\n }\n });\n }\n\n private setupUnhandledRejectionHandler(): void {\n // React Native supports the standard unhandledrejection event\n this.unhandledRejectionListener = (event: PromiseRejectionEvent) => {\n try {\n const reason = event.reason;\n\n // Convert reason to an Error if it isn't one (matches Web SDK approach)\n const isPrimitiveRejection = !(reason instanceof Error);\n let error: Error;\n if (reason instanceof Error) {\n error = reason;\n } else {\n error = new Error(\n `Unhandled Promise Rejection: ${typeof reason === 'object' ? JSON.stringify(reason) : String(reason)}`\n );\n }\n\n // Check if error should be ignored\n if (this.shouldIgnoreError(error)) {\n return;\n }\n\n // Check for duplicate errors\n if (this.options.enableDeduplication && this.isDuplicateError(error)) {\n return;\n }\n\n // Enhance error with React Native context and stack frames\n const {\n error: enhancedError,\n stackFrames,\n context,\n } = enhanceErrorWithContext(\n error,\n {\n mechanism: ErrorMechanism.UNHANDLED_REJECTION,\n },\n { releaseBundleFilename: this.options.releaseBundleFilename }\n );\n\n // Type: use actual error type for Error objects; 'UnhandledRejection' for primitives (Web SDK)\n const errorType = isPrimitiveRejection ? primitiveUnhandledType : enhancedError.name || 'Error';\n\n this.api.pushError(enhancedError, {\n type: errorType,\n context,\n stackFrames,\n });\n\n // Track error fingerprint for deduplication\n if (this.options.enableDeduplication) {\n this.addErrorFingerprint(error);\n }\n } catch (_e) {\n // Don't let error reporting cause more errors\n }\n };\n\n // Add the listener\n global.addEventListener?.('unhandledrejection', this.unhandledRejectionListener);\n }\n\n /**\n * Check if error should be ignored based on ignoreErrors patterns\n */\n private shouldIgnoreError(error: Error): boolean {\n if (!error || !error.message) {\n return false;\n }\n\n return this.options.ignoreErrors.some((pattern) => pattern.test(error.message));\n }\n\n /**\n * Check if error is a duplicate based on message and stack\n */\n private isDuplicateError(error: Error): boolean {\n const message = error.message || '';\n const stack = error.stack || '';\n const now = Date.now();\n\n // Clean up old entries first\n this.cleanupOldFingerprints(now);\n\n // Check if we've seen this error recently\n return this.errorFingerprints.some((fingerprint) => {\n return (\n fingerprint.message === message &&\n fingerprint.stack === stack &&\n now - fingerprint.timestamp < this.options.deduplicationWindow\n );\n });\n }\n\n /**\n * Add error fingerprint for deduplication tracking\n */\n private addErrorFingerprint(error: Error): void {\n const message = error.message || '';\n const stack = error.stack || '';\n const timestamp = Date.now();\n\n this.errorFingerprints.push({ message, stack, timestamp });\n\n // Limit the number of tracked fingerprints\n if (this.errorFingerprints.length > this.options.maxDeduplicationEntries) {\n this.errorFingerprints.shift();\n }\n }\n\n /**\n * Remove error fingerprints older than the deduplication window\n */\n private cleanupOldFingerprints(now: number): void {\n this.errorFingerprints = this.errorFingerprints.filter(\n (fingerprint) => now - fingerprint.timestamp < this.options.deduplicationWindow\n );\n }\n\n unpatch(): void {\n // Restore original error handler\n if (this.originalErrorHandler) {\n global.ErrorUtils.setGlobalHandler(this.originalErrorHandler);\n }\n\n // Remove unhandled rejection listener\n if (this.unhandledRejectionListener) {\n global.removeEventListener?.('unhandledrejection', this.unhandledRejectionListener);\n }\n\n // Clear deduplication tracking\n this.errorFingerprints = [];\n }\n}\n"]}