@dreamhorizonorg/pulse-react-native 0.0.1 → 0.0.2

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 (135) hide show
  1. package/PulseReactNativeOtel.podspec +1 -1
  2. package/README.md +34 -879
  3. package/android/build.gradle +10 -15
  4. package/android/proguard-rules.pro +3 -99
  5. package/android/src/main/java/com/pulsereactnativeotel/Pulse.kt +87 -0
  6. package/android/src/main/java/com/pulsereactnativeotel/PulseOtelConstants.kt +1 -1
  7. package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelLogger.kt +3 -1
  8. package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelModule.kt +53 -3
  9. package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelPackage.kt +1 -1
  10. package/android/src/main/java/com/pulsereactnativeotel/PulseReactNativeOtelTracer.kt +24 -8
  11. package/android/src/main/java/com/pulsereactnativeotel/ReactNativeScreenAttributesLogRecordProcessor.kt +21 -0
  12. package/android/src/main/java/com/pulsereactnativeotel/ReactNativeScreenAttributesSpanProcessor.kt +30 -0
  13. package/android/src/main/java/com/pulsereactnativeotel/ReactNativeScreenNameTracker.kt +17 -0
  14. package/app.plugin.js +1 -0
  15. package/ios/PulseReactNativeOtel.mm +7 -1
  16. package/lib/module/NativePulseReactNativeOtel.js.map +1 -1
  17. package/lib/module/config.js +29 -9
  18. package/lib/module/config.js.map +1 -1
  19. package/lib/module/errorBoundary.js.map +1 -1
  20. package/lib/module/events.js +6 -0
  21. package/lib/module/events.js.map +1 -1
  22. package/lib/module/index.js +4 -2
  23. package/lib/module/index.js.map +1 -1
  24. package/lib/module/navigation/index.js +172 -0
  25. package/lib/module/navigation/index.js.map +1 -0
  26. package/lib/module/navigation/navigation.interface.js +2 -0
  27. package/lib/module/navigation/navigation.interface.js.map +1 -0
  28. package/lib/module/navigation/screen-interactive.js +101 -0
  29. package/lib/module/navigation/screen-interactive.js.map +1 -0
  30. package/lib/module/navigation/screen-load.js +68 -0
  31. package/lib/module/navigation/screen-load.js.map +1 -0
  32. package/lib/module/navigation/screen-session.js +60 -0
  33. package/lib/module/navigation/screen-session.js.map +1 -0
  34. package/lib/module/navigation/useNavigationTracking.js +33 -0
  35. package/lib/module/navigation/useNavigationTracking.js.map +1 -0
  36. package/lib/module/navigation/utils.js +17 -0
  37. package/lib/module/navigation/utils.js.map +1 -0
  38. package/lib/module/network-interceptor/graphql-helper.js +92 -0
  39. package/lib/module/network-interceptor/graphql-helper.js.map +1 -0
  40. package/lib/module/network-interceptor/request-tracker-xhr.js +2 -1
  41. package/lib/module/network-interceptor/request-tracker-xhr.js.map +1 -1
  42. package/lib/module/network-interceptor/span-helpers.js +24 -16
  43. package/lib/module/network-interceptor/span-helpers.js.map +1 -1
  44. package/lib/module/network-interceptor/url-helper.js +58 -2
  45. package/lib/module/network-interceptor/url-helper.js.map +1 -1
  46. package/lib/module/pulse.constants.js +42 -0
  47. package/lib/module/pulse.constants.js.map +1 -0
  48. package/lib/module/trace.js +17 -2
  49. package/lib/module/trace.js.map +1 -1
  50. package/lib/typescript/plugin/src/index.d.ts +5 -0
  51. package/lib/typescript/plugin/src/index.d.ts.map +1 -0
  52. package/lib/typescript/plugin/src/types.d.ts +27 -0
  53. package/lib/typescript/plugin/src/types.d.ts.map +1 -0
  54. package/lib/typescript/plugin/src/utils.d.ts +10 -0
  55. package/lib/typescript/plugin/src/utils.d.ts.map +1 -0
  56. package/lib/typescript/plugin/src/withAndroidPulse.d.ts +4 -0
  57. package/lib/typescript/plugin/src/withAndroidPulse.d.ts.map +1 -0
  58. package/lib/typescript/src/NativePulseReactNativeOtel.d.ts +8 -2
  59. package/lib/typescript/src/NativePulseReactNativeOtel.d.ts.map +1 -1
  60. package/lib/typescript/src/config.d.ts +8 -2
  61. package/lib/typescript/src/config.d.ts.map +1 -1
  62. package/lib/typescript/src/errorBoundary.d.ts.map +1 -1
  63. package/lib/typescript/src/events.d.ts.map +1 -1
  64. package/lib/typescript/src/index.d.ts +5 -3
  65. package/lib/typescript/src/index.d.ts.map +1 -1
  66. package/lib/typescript/src/navigation/index.d.ts +12 -0
  67. package/lib/typescript/src/navigation/index.d.ts.map +1 -0
  68. package/lib/typescript/src/navigation/navigation.interface.d.ts +17 -0
  69. package/lib/typescript/src/navigation/navigation.interface.d.ts.map +1 -0
  70. package/lib/typescript/src/navigation/screen-interactive.d.ts +16 -0
  71. package/lib/typescript/src/navigation/screen-interactive.d.ts.map +1 -0
  72. package/lib/typescript/src/navigation/screen-load.d.ts +13 -0
  73. package/lib/typescript/src/navigation/screen-load.d.ts.map +1 -0
  74. package/lib/typescript/src/navigation/screen-session.d.ts +15 -0
  75. package/lib/typescript/src/navigation/screen-session.d.ts.map +1 -0
  76. package/lib/typescript/src/navigation/useNavigationTracking.d.ts +5 -0
  77. package/lib/typescript/src/navigation/useNavigationTracking.d.ts.map +1 -0
  78. package/lib/typescript/src/navigation/utils.d.ts +8 -0
  79. package/lib/typescript/src/navigation/utils.d.ts.map +1 -0
  80. package/lib/typescript/src/network-interceptor/graphql-helper.d.ts +8 -0
  81. package/lib/typescript/src/network-interceptor/graphql-helper.d.ts.map +1 -0
  82. package/lib/typescript/src/network-interceptor/request-tracker-xhr.d.ts.map +1 -1
  83. package/lib/typescript/src/network-interceptor/span-helpers.d.ts +1 -1
  84. package/lib/typescript/src/network-interceptor/span-helpers.d.ts.map +1 -1
  85. package/lib/typescript/src/network-interceptor/url-helper.d.ts +9 -0
  86. package/lib/typescript/src/network-interceptor/url-helper.d.ts.map +1 -1
  87. package/lib/typescript/src/pulse.constants.d.ts +35 -0
  88. package/lib/typescript/src/pulse.constants.d.ts.map +1 -0
  89. package/lib/typescript/src/pulse.interface.d.ts +2 -1
  90. package/lib/typescript/src/pulse.interface.d.ts.map +1 -1
  91. package/lib/typescript/src/trace.d.ts +7 -0
  92. package/lib/typescript/src/trace.d.ts.map +1 -1
  93. package/package.json +29 -9
  94. package/plugin/build/index.d.ts +4 -0
  95. package/plugin/build/index.js +10 -0
  96. package/plugin/build/types.d.ts +26 -0
  97. package/plugin/build/types.js +2 -0
  98. package/plugin/build/utils.d.ts +9 -0
  99. package/plugin/build/utils.js +102 -0
  100. package/plugin/build/withAndroidPulse.d.ts +3 -0
  101. package/plugin/build/withAndroidPulse.js +53 -0
  102. package/scripts/pulse-cli.js +82 -0
  103. package/scripts/uploadService.js +122 -0
  104. package/scripts/utils.js +125 -0
  105. package/src/NativePulseReactNativeOtel.ts +11 -2
  106. package/src/config.ts +37 -8
  107. package/src/errorBoundary.tsx +11 -5
  108. package/src/events.ts +7 -0
  109. package/src/global.d.ts +0 -1
  110. package/src/index.tsx +6 -3
  111. package/src/navigation/index.ts +306 -0
  112. package/src/navigation/navigation.interface.ts +19 -0
  113. package/src/navigation/screen-interactive.ts +149 -0
  114. package/src/navigation/screen-load.ts +103 -0
  115. package/src/navigation/screen-session.ts +87 -0
  116. package/src/navigation/useNavigationTracking.ts +50 -0
  117. package/src/navigation/utils.ts +19 -0
  118. package/src/network-interceptor/graphql-helper.ts +110 -0
  119. package/src/network-interceptor/request-tracker-xhr.ts +3 -1
  120. package/src/network-interceptor/span-helpers.ts +27 -18
  121. package/src/network-interceptor/url-helper.ts +67 -1
  122. package/src/pulse.constants.ts +38 -0
  123. package/src/pulse.interface.ts +6 -1
  124. package/src/trace.ts +25 -2
  125. package/LICENSE +0 -20
  126. package/lib/module/network-interceptor/request-tracker-fetch.js +0 -72
  127. package/lib/module/network-interceptor/request-tracker-fetch.js.map +0 -1
  128. package/lib/module/reactNavigation.js +0 -100
  129. package/lib/module/reactNavigation.js.map +0 -1
  130. package/lib/typescript/src/network-interceptor/request-tracker-fetch.d.ts +0 -7
  131. package/lib/typescript/src/network-interceptor/request-tracker-fetch.d.ts.map +0 -1
  132. package/lib/typescript/src/reactNavigation.d.ts +0 -10
  133. package/lib/typescript/src/reactNavigation.d.ts.map +0 -1
  134. package/src/network-interceptor/request-tracker-fetch.ts +0 -96
  135. package/src/reactNavigation.tsx +0 -146
package/README.md CHANGED
@@ -1,900 +1,55 @@
1
- <p align="center">
2
- <h1 align="center">Pulse React Native SDK</h1>
3
- </p>
1
+ <div align="center">
4
2
 
5
- <p align="center">
6
- <strong>Production-grade observability for React Native applications</strong>
7
- </p>
3
+ # Pulse React Native SDK
8
4
 
9
- <p align="center">
10
- Real-time monitoring, error tracking, and performance insights powered by OpenTelemetry
11
- </p>
5
+ **Production-grade observability for React Native applications**
12
6
 
13
- <p align="center">
14
- <code>@dreamhorizonorg/pulse-react-native</code>
15
- </p>
7
+ *Real-time monitoring, error tracking, and performance insights powered by OpenTelemetry*
16
8
 
17
- ---
18
-
19
- ## Features
20
-
21
- - 🚨 **Error Monitoring**: Capture JavaScript crashes and exceptions with full stack traces.
22
-
23
- - ⚡ **Performance Monitoring**: Distributed tracing spans for synchronous and asynchronous operations with automatic or manual instrumentation.
24
-
25
- - 🌐 **Network Monitoring**: Auto-instrument HTTP requests (fetch and XMLHttpRequest) with zero code changes.
26
-
27
- - 🧭 **Navigation Tracking**: Automatic screen transition monitoring with React Navigation integration.
28
-
29
- - 📊 **Event Tracking**: Log custom business events and user actions with structured metadata.
30
-
31
- - 🔌 **OpenTelemetry Native**: Built on OpenTelemetry Android SDK. Automatically captures ANR, frozen frames, activity/fragment lifecycle, network changes, view interactions, and more. See the [Android SDK documentation](../pulse-android-otel) for all native features.
32
-
33
- - 🏗️ **Architecture Support**: Supports both **React Native Old Architecture** and **New Architecture** out of the box.
34
-
35
- > **Note:** Currently supports Android only. iOS support is coming soon.
36
-
37
- ---
38
-
39
- ## Quick Start
40
-
41
- ### Installation
42
-
43
- ```sh
44
- npm install @dreamhorizonorg/pulse-react-native
45
- # or
46
- yarn add @dreamhorizonorg/pulse-react-native
47
- ```
48
-
49
- ### Initialization
50
-
51
- #### 1. Android Native Setup
52
-
53
- Initialize the Pulse Android SDK in your `MainApplication.kt`:
54
-
55
- ```kotlin
56
- import android.app.Application
57
- import com.pulse.android.sdk.PulseSDK
58
-
59
- class MainApplication : Application() {
60
- override fun onCreate() {
61
- super.onCreate()
62
-
63
- // Initialize Pulse Android SDK
64
- PulseSDK.INSTANCE.initialize(
65
- application = this,
66
- endpointBaseUrl = <server-url>
67
- )
68
- }
69
- }
70
- ```
71
-
72
- > **Important:** This step is mandatory. Without native SDK initialization, no telemetry will be sent.
73
-
74
- **Advanced Configuration:**
75
- For custom endpoints, headers, disk buffering, session settings, ANR thresholds, and other native features, see the [Android SDK Initialization Guide](../pulse-android-otel#initialization).
76
-
77
- > **Note:** iOS support is coming soon.
78
-
79
- #### 2. React Native Auto-Instrumentation
80
-
81
- Enable automatic instrumentation in your app entry point (e.g., `App.tsx`):
82
-
83
- ```typescript
84
- import { Pulse } from '@dreamhorizonorg/pulse-react-native';
85
-
86
- // Enable auto-instrumentation features
87
- Pulse.start();
88
-
89
- function App() {
90
- // Your app code
91
- }
92
- ```
93
-
94
- **What gets automatically tracked:**
95
- - ✅ JavaScript crashes and unhandled exceptions
96
- - ✅ HTTP requests via fetch and XMLHttpRequest
97
-
98
- > **Note:** All `autoInstrument*` options are enabled by default. You can disable specific features by setting them to `false`.
99
-
100
- ---
101
-
102
- ## Usage Guide
103
-
104
- ### Auto-Instrumentation
105
-
106
- Pulse React Native automatically captures critical application events without requiring manual instrumentation. Simply call `Pulse.start()` to enable automatic monitoring:
107
-
108
- ```typescript
109
- Pulse.start(); // All auto-instrumentation enabled by default
110
- ```
111
-
112
- #### What Gets Automatically Tracked
113
-
114
- **1. Unhandled JavaScript Errors**
115
-
116
- All unhandled JavaScript exceptions and promise rejections are automatically captured with full stack traces. This includes:
117
- - Runtime errors and crashes
118
- - Unhandled promise rejections
119
- - Fatal and non-fatal exceptions
120
- - Complete JavaScript call stacks
121
-
122
- The error handler integrates seamlessly with React Native's `ErrorUtils`, preserving the original error handling chain while capturing telemetry.
123
-
124
- **2. Network Requests**
125
-
126
- HTTP requests are automatically instrumented when using standard APIs:
127
- - `fetch()` API calls
128
- - `XMLHttpRequest` (XHR) operations
129
-
130
- Each network request captures:
131
- - HTTP method (GET, POST, etc.)
132
- - Request URL (full URL, scheme, host, path)
133
- - Response status code
134
- - Request type (fetch or xmlhttprequest)
135
- - Platform (Android/iOS)
136
- - Network errors with error messages and stack traces
137
-
138
- **3. React Navigation**
139
-
140
- Screen navigation events are tracked when using [React Navigation](https://reactnavigation.org/) (requires setup, see [React Navigation Integration](#react-navigation-integration)):
141
- - Screen-to-screen transitions
142
- - Screen names and route parameters
143
- - Navigation history and patterns
144
-
145
- #### Disabling Auto-Instrumentation
146
-
147
- You can selectively disable specific auto-instrumentation features:
148
-
149
- ```typescript
150
- Pulse.start({
151
- autoDetectExceptions: false, // Disable crash tracking
152
- autoDetectNetwork: false, // Disable network monitoring
153
- autoDetectNavigation: false // Disable navigation tracking
154
- });
155
- ```
156
-
157
- > **Note:** Disabling auto-instrumentation means you'll need to manually track these events if needed.
158
-
159
- ### React Navigation Integration
160
-
161
- Pulse automatically tracks screen navigation and route changes when using [React Navigation](https://reactnavigation.org/).
162
-
163
- #### Setup
164
-
165
- ```typescript
166
- import { NavigationContainer } from '@react-navigation/native';
167
- import { Pulse } from '@dreamhorizonorg/pulse-react-native';
168
-
169
- function App() {
170
- const navigationIntegration = Pulse.createNavigationIntegration();
171
-
172
- return (
173
- <NavigationContainer
174
- onReady={(ref) => navigationIntegration.registerNavigationContainer(ref)}
175
- >
176
- {/* Your screens */}
177
- </NavigationContainer>
178
- );
179
- }
180
- ```
181
-
182
- #### Supported Navigators
183
-
184
- - **[Stack Navigator](https://reactnavigation.org/docs/stack-navigator)** - JavaScript-based stack navigation
185
- - **[Native Stack Navigator](https://reactnavigation.org/docs/native-stack-navigator)** - Native platform navigation
186
-
187
- #### Captured Data
188
-
189
- Each navigation event includes:
190
-
191
- ```typescript
192
- {
193
- 'screen.name': 'ProfileScreen', // Current screen
194
- 'last.screen.name': 'HomeScreen', // Previous screen
195
- 'routeHasBeenSeen': false, // First visit or returning
196
- 'routeKey': 'ProfileScreen-abc123', // Unique route identifier
197
- 'pulse.type': 'screen_load', // Event type
198
- 'phase': 'start' // Navigation phase
199
- }
200
- ```
201
-
202
- **Requirements:**
203
- - `@react-navigation/native` v5.x or higher
204
- - `autoDetectNavigation: true` in `Pulse.start()` (enabled by default)
205
-
206
- ### Reporting Unhandled Errors
207
-
208
- Unhandled JavaScript errors and promise rejections are automatically captured when `autoDetectExceptions` is enabled (default).
209
-
210
- #### Automatic Capture
211
-
212
- All uncaught errors are reported with:
213
- - Full JavaScript stack traces
214
- - Error type and message
215
- - Platform information
216
- - Timestamp and context
217
-
218
- #### Manual Error Reporting
219
-
220
- You can manually report caught exceptions:
221
-
222
- ```typescript
223
- try {
224
- await riskyOperation();
225
- } catch (error) {
226
- Pulse.reportException(error);
227
- }
228
- ```
229
-
230
- **Report as fatal:**
231
-
232
- ```typescript
233
- Pulse.reportException(error, true); // Second parameter: isFatal
234
- ```
235
-
236
- **With custom attributes:**
237
-
238
- ```typescript
239
- Pulse.reportException(error, false, {
240
- userId: '123',
241
- operation: 'checkout',
242
- attempt: 3,
243
- environment: 'production'
244
- });
245
- ```
246
-
247
- **Supported attribute types:** `string`, `number`, `boolean`, and arrays of these types.
248
-
249
- ### Reporting Render Errors
250
-
251
- Pulse provides a built-in `ErrorBoundary` component that uses [React's Error Boundary API](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) to automatically catch and report errors from inside a React component tree.
252
-
253
- #### Basic Usage
254
-
255
- ```typescript
256
- import { Pulse } from '@dreamhorizonorg/pulse-react-native';
257
- import { View, Text } from 'react-native';
258
-
259
- function App() {
260
- return (
261
- <Pulse.ErrorBoundary fallback={<Text>Something went wrong</Text>}>
262
- {/* Your app */}
263
- </Pulse.ErrorBoundary>
264
- );
265
- }
266
- ```
267
-
268
- #### Fallback Component
269
-
270
- You can provide a custom fallback UI with error details:
271
-
272
- ```typescript
273
- function ErrorFallback({ error, componentStack }) {
274
- return (
275
- <View>
276
- <Text>An error occurred</Text>
277
- <Text>{error.toString()}</Text>
278
- </View>
279
- );
280
- }
281
-
282
- function App() {
283
- return (
284
- <Pulse.ErrorBoundary fallback={ErrorFallback}>
285
- {/* Your app */}
286
- </Pulse.ErrorBoundary>
287
- );
288
- }
289
- ```
290
-
291
- #### Custom Error Handler
292
-
293
- Use the `onError` callback for additional error handling logic:
294
-
295
- ```typescript
296
- <Pulse.ErrorBoundary
297
- fallback={ErrorFallback}
298
- onError={(error, componentStack) => {
299
- // Custom logging or side effects
300
- console.log('Render error:', error);
301
- console.log('Component stack:', componentStack);
302
- }}
303
- >
304
- {/* Your app */}
305
- </Pulse.ErrorBoundary>
306
- ```
307
-
308
- #### Higher-Order Component (HOC)
309
-
310
- Wrap individual components using the HOC pattern:
311
-
312
- ```typescript
313
- import { Pulse } from '@dreamhorizonorg/pulse-react-native';
314
-
315
- const MyComponent = () => {
316
- // Component code
317
- };
318
-
319
- export default Pulse.withErrorBoundary(MyComponent, {
320
- fallback: <Text>Error occurred</Text>,
321
- onError: (error, componentStack) => {
322
- console.log('Component error:', error);
323
- }
324
- });
325
- ```
326
-
327
- **How it works:**
328
- - Errors are automatically reported to Pulse with full stack traces
329
- - If a `fallback` is provided, the error is marked as handled (non-fatal)
330
- - If no `fallback` is provided, the error is marked as unhandled (fatal)
331
-
332
- > **Note:** In development mode, React will rethrow errors caught by error boundaries. This may result in errors being reported twice. We recommend testing error boundaries with production builds.
333
-
334
- ### Reporting ANRs and Frozen Frames
335
-
336
- ANR (Application Not Responding) and frozen frame detection are automatically handled by the native Android SDK.
337
-
338
- **What's detected:**
339
- - **Android ANRs** - When the main thread blocks for too long
340
- - **Frozen Frames** - When rendering takes longer than expected
341
- - **Slow Renders** - UI performance bottlenecks
342
-
343
- **Configuration:**
344
- ANR and frozen frame detection are enabled by default. To customize thresholds, detection intervals, or disable specific checks, see the Android SDK documentation:
345
- - [ANR Detection](../pulse-android-otel/instrumentation/anr)
346
- - [Slow Rendering Detection](../pulse-android-otel/instrumentation/slowrendering)
347
-
348
- ### Tracking CodePush Deployments
349
-
350
- If your app uses over-the-air (OTA) updates like [Microsoft App Center CodePush](https://github.com/microsoft/react-native-code-push) or [Delivr DOTA](https://github.com/ds-horizon/delivr-sdk-ota), you can track which JavaScript bundle version is running by setting a global attribute.
351
-
352
- #### Why Track Code Bundle IDs?
353
-
354
- With OTA updates, the same native app version can run multiple different JavaScript bundles. Tracking the code bundle ID helps you:
355
- - **Identify deployment-specific issues**: Pinpoint which OTA release caused an error
356
- - **Track update adoption**: See how many users are on each bundle version
357
- - **Debug effectively**: Match errors to the exact code version and source maps
358
-
359
- #### Setting Code Bundle ID
360
-
361
- Use `Pulse.setGlobalAttribute()` to pass the code bundle ID from your OTA provider:
362
-
363
- ```typescript
364
- import codePush from '@d11/dota';
365
- import { Pulse } from '@dreamhorizonorg/pulse-react-native';
366
-
367
- // Set on app start
368
- codePush.getUpdateMetadata().then(update => {
369
- if (update?.label) {
370
- Pulse.setGlobalAttribute('codeBundleId', update.label);
371
- }
372
- });
373
- ```
374
-
375
- All subsequent events, errors, and spans will automatically include the `codeBundleId` attribute, allowing you to filter and analyze telemetry data by deployment version.
376
-
377
- > **Tip:** For best results, set the code bundle ID before calling `Pulse.start()` to ensure all telemetry includes this identifier.
378
-
379
- ### Setting Global Attributes
380
-
381
- Global attributes are automatically attached to all telemetry data (events, errors, spans) throughout your application's lifecycle.
382
-
383
- > **Note:** Global attributes set via `setGlobalAttribute` only apply to telemetry originating from the React Native side. Native Android events (ANR, frozen frames, activity lifecycle, etc.) are not affected. To set global attributes for native Android telemetry, refer to the [Pulse Android SDK initialization guide](https://github.com/ds-horizon/pulse/tree/feat/interaction-reactive/pulse-android-otel#agent-initialization).
384
-
385
- #### Basic Usage
386
-
387
- ```typescript
388
- import { Pulse } from '@dreamhorizonorg/pulse-react-native';
389
-
390
- // Set global attributes
391
- Pulse.setGlobalAttribute('environment', 'production');
392
- Pulse.setGlobalAttribute('buildNumber', '1234');
393
-
394
- // All subsequent telemetry will include these attributes
395
- Pulse.trackEvent('user_login', { userId: '123' });
396
- ```
397
-
398
- #### Practical Example
399
-
400
- ```typescript
401
- import DeviceInfo from 'react-native-device-info';
402
- import { Pulse } from '@dreamhorizonorg/pulse-react-native';
403
-
404
- // Set once at app start
405
- Pulse.setGlobalAttribute('appVersion', DeviceInfo.getVersion());
406
- Pulse.setGlobalAttribute('environment', __DEV__ ? 'development' : 'production');
407
- Pulse.setGlobalAttribute('userTier', 'premium');
408
- ```
409
-
410
- #### Attribute Priority
411
-
412
- Event/span-specific attributes override global attributes when keys conflict:
413
-
414
- ```typescript
415
- Pulse.setGlobalAttribute('environment', 'production');
416
-
417
- // This event's 'environment' will be 'staging'
418
- Pulse.trackEvent('test', { environment: 'staging' });
419
- ```
420
-
421
- **Common use cases:** CodePush labels, build numbers, environment flags, feature flags, user segments, device metadata.
9
+ [![npm version](https://img.shields.io/npm/v/@dreamhorizonorg/pulse-react-native.svg)](https://www.npmjs.com/package/@dreamhorizonorg/pulse-react-native)
10
+ [![npm downloads](https://img.shields.io/npm/dm/@dreamhorizonorg/pulse-react-native.svg)](https://www.npmjs.com/package/@dreamhorizonorg/pulse-react-native)
11
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](../LICENSE)
422
12
 
423
- ### Setting User Properties
424
-
425
- Associate telemetry with specific users:
426
-
427
- ```typescript
428
- // Set user ID
429
- Pulse.setUserId('user-12345');
430
-
431
- // Set individual properties
432
- Pulse.setUserProperty('email', 'user@example.com');
433
- Pulse.setUserProperty('plan', 'premium');
434
-
435
- // Set multiple properties at once
436
- Pulse.setUserProperties({
437
- email: 'user@example.com',
438
- plan: 'premium',
439
- signupDate: '2024-01-15',
440
- verified: true
441
- });
442
-
443
- // Clear user on logout
444
- Pulse.setUserId(null);
445
- ```
446
-
447
- ### Custom Instrumentation
448
-
449
- Create custom performance traces (spans) to measure the execution time of specific operations in your application. Spans help you understand which parts of your code are slow and need optimization.
450
-
451
- #### Understanding Spans
452
-
453
- A **span** represents a unit of work or operation. Each span has:
454
- - A **name**: Describes what the span measures (e.g., `'screen_render'`, `'image_upload'`, `'payment_flow'`)
455
- - A **start time**: When the operation began
456
- - An **end time**: When the operation completed
457
- - **Attributes**: Additional context (e.g., `{ screenName: 'Checkout', itemCount: 3 }`)
458
- - **Events**: Milestones within the span (e.g., `'validation_passed'`, `'api_call_started'`, `'ui_rendered'`)
459
-
460
- When you start a span, it becomes **active**, meaning it's the current span being tracked. Any errors that occur while a span is active are automatically associated with that span.
461
-
462
- #### Automatic Span Management: `trackSpan()` (Recommended)
463
-
464
- The `trackSpan()` function automatically manages the span lifecycle for you. It starts the span, executes your code, and ends the span when done—even if an error occurs. This works with both synchronous and asynchronous operations.
465
-
466
- **Synchronous operation:**
467
-
468
- ```typescript
469
- import { Pulse } from '@dreamhorizonorg/pulse-react-native';
470
-
471
- const result = Pulse.trackSpan('calculate_total',
472
- { attributes: { itemCount: 5 } },
473
- () => {
474
- let total = 0;
475
- for (let i = 0; i < 1000; i++) {
476
- total += i;
477
- }
478
- return total;
479
- }
480
- );
481
- ```
482
-
483
- **Asynchronous operation:**
484
-
485
- ```typescript
486
- const users = await Pulse.trackSpan('fetch_users',
487
- { attributes: { limit: 100 } },
488
- async () => {
489
- const response = await fetch('https://api.example.com/users?limit=100');
490
- return response.json();
491
- }
492
- );
493
- ```
494
-
495
- #### Manual Span Control: `startSpan()`
496
-
497
- Use `startSpan()` when you need fine-grained control over the span lifecycle. This is useful for:
498
- - Long-running operations with multiple phases
499
- - Operations where you need to add events or update attributes dynamically
500
- - Scenarios where the span end time isn't tied to a single function
501
-
502
- **Basic example:**
503
-
504
- ```typescript
505
- const span = Pulse.startSpan('image_processing', {
506
- attributes: { imageId: 'img-123', format: 'jpeg' }
507
- });
508
-
509
- try {
510
- span.addEvent('resize_started');
511
- await resizeImage();
512
- span.addEvent('resize_completed', { newSize: '800x600' });
513
-
514
- span.addEvent('compression_started');
515
- await compressImage();
516
- span.addEvent('compression_completed');
517
-
518
- span.setAttributes({ finalSize: 245000, compressionRatio: 0.65 });
519
- } catch (error) {
520
- span.recordException(error);
521
- span.setAttributes({ status: 'failed' });
522
- } finally {
523
- span.end(); // Always end the span
524
- }
525
- ```
526
-
527
- **Span API:**
528
-
529
- ```typescript
530
- import { Pulse, SpanStatusCode } from '@dreamhorizonorg/pulse-react-native';
531
-
532
- // Add a single event
533
- span.addEvent('cache_miss');
534
-
535
- // Add event with attributes
536
- span.addEvent('retry_attempt', { attemptNumber: 2, delayMs: 1000 });
537
-
538
- // Update span attributes
539
- span.setAttributes({
540
- recordsProcessed: 150,
541
- cacheHitRate: 0.85
542
- });
543
-
544
- // Record an exception
545
- try {
546
- await riskyOperation();
547
- } catch (error) {
548
- span.recordException(error);
549
- span.end(SpanStatusCode.ERROR); // Mark span as failed
550
- }
551
-
552
- // End the span with status
553
- span.end(); // Default: SpanStatusCode.UNSET
554
- span.end(SpanStatusCode.OK); // Explicitly mark as successful
555
- span.end(SpanStatusCode.ERROR); // Mark as failed
556
- ```
557
-
558
- #### Span Status Codes
559
-
560
- You can optionally set a status code when ending a span to indicate the outcome:
561
-
562
- | Status | Description | When to Use |
563
- |--------|-------------|-------------|
564
- | `SpanStatusCode.OK` | Operation completed successfully | Successful operations, no errors |
565
- | `SpanStatusCode.ERROR` | Operation failed or encountered an error | Failures, exceptions, validation errors |
566
- | `SpanStatusCode.UNSET` | Status not specified (default) | When outcome is unknown or not applicable |
567
-
568
- **Example with status codes:**
569
-
570
- ```typescript
571
- import { Pulse, SpanStatusCode } from '@dreamhorizonorg/pulse-react-native';
572
-
573
- const span = Pulse.startSpan('payment_processing');
574
-
575
- try {
576
- await processPayment();
577
- span.end(SpanStatusCode.OK); // Success
578
- } catch (error) {
579
- span.recordException(error);
580
- span.end(SpanStatusCode.ERROR); // Failure
581
- }
582
- ```
583
-
584
- #### Best Practices
585
-
586
- 1. **Use descriptive span names**: `'fetch_user_profile'` is better than `'api_call'`
587
- 2. **Add meaningful attributes**: Include IDs, counts, types, or other context
588
- 3. **Always end manual spans**: Use `try/finally` to ensure `span.end()` is called
589
- 4. **Record exceptions**: Call `span.recordException(error)` to capture errors
590
- 5. **Add events for milestones**: Track key points like `'cache_hit'`, `'retry_started'`, `'validation_passed'`
591
-
592
- > **Note:** Spans created with `startSpan()` must be manually ended with `span.end()`. Forgetting to end a span will result in incomplete telemetry data.
593
-
594
- ### Tracking Custom Events
595
-
596
- Track custom business events, user actions, and application milestones to gain insights into user behavior and application usage patterns.
597
-
598
- #### Basic Usage
599
-
600
- ```typescript
601
- import { Pulse } from '@dreamhorizonorg/pulse-react-native';
602
-
603
- // Simple event without attributes
604
- Pulse.trackEvent('app_opened');
605
- Pulse.trackEvent('user_logout');
606
- ```
607
-
608
- #### Events with Attributes
609
-
610
- Add context to your events with attributes:
611
-
612
- ```typescript
613
- // E-commerce events
614
- Pulse.trackEvent('product_viewed', {
615
- productId: 'SKU-12345',
616
- category: 'electronics',
617
- price: 299.99,
618
- inStock: true
619
- });
620
- ```
13
+ </div>
621
14
 
622
15
  ---
623
16
 
624
- ## API Reference
625
-
626
- ### Initialization
17
+ ## Key Features
627
18
 
628
- | Method | Parameters | Returns | Description |
629
- |--------|------------|---------|-------------|
630
- | `Pulse.start(options?)` | `options?: PulseStartOptions` | `void` | Initialize auto-instrumentation. All options default to `true`. |
631
- | `Pulse.isInitialized()` | - | `boolean` | Check if native SDK is initialized. |
632
-
633
- **PulseStartOptions:**
634
- ```typescript
635
- {
636
- autoDetectExceptions?: boolean; // Auto-detect JS crashes & errors
637
- autoDetectNavigation?: boolean; // Auto-detect navigation
638
- autoDetectNetwork?: boolean; // Auto-detect HTTP requests
639
- }
640
- ```
19
+ - **Error Monitoring** - Capture JavaScript crashes and exceptions with full stack traces
20
+ - **Performance Monitoring** - Distributed tracing spans for synchronous and asynchronous operations
21
+ - **Network Monitoring** - Auto-instrument HTTP requests (fetch and XMLHttpRequest) with zero code changes
22
+ - **Navigation Tracking** - Automatic screen transition monitoring with React Navigation integration
23
+ - **Event Tracking** - Log custom business events and user actions with structured metadata
24
+ - **OpenTelemetry Native** - Built on OpenTelemetry Android SDK. Automatically captures ANR, frozen frames, lifecycle events, and more
25
+ - **Architecture Support** - Supports both React Native Old Architecture and New Architecture out of the box
641
26
 
642
27
  ---
643
28
 
644
- ### Error Tracking
29
+ ## 📚 Documentation
645
30
 
646
- | Method | Parameters | Returns | Description |
647
- |--------|------------|---------|-------------|
648
- | `Pulse.reportException(error, isFatal?, attributes?)` | `error: Error \| string`<br/>`isFatal?: boolean`<br/>`attributes?: PulseAttributes` | `void` | Report an exception. Default `isFatal: false`. |
31
+ Complete documentation is available at **[dreamhorizon.org](https://pulse.dreamhorizon.org/docs/sdk/react-native/overview)**.
649
32
 
650
- ---
33
+ ### Getting Started
34
+ - [Overview](https://pulse.dreamhorizon.org/docs/sdk/react-native/overview) - Introduction and key features
35
+ - [Quick Start](https://pulse.dreamhorizon.org/docs/sdk/react-native/installation) - Installation and setup guide
651
36
 
652
- ### Events
37
+ ### Instrumentation
38
+ - [Network Instrumentation](https://pulse.dreamhorizon.org/docs/sdk/react-native/instrumentation/network) - Auto-instrument HTTP requests
39
+ - [Error Instrumentation](https://pulse.dreamhorizon.org/docs/sdk/react-native/instrumentation/errors) - Error tracking and reporting
40
+ <!-- - [Navigation Instrumentation](https://dreamhorizon.org/docs/sdk/react-native/instrumentation/navigation-instrumentation) - React Navigation integration -->
41
+ - [Custom Instrumentation](https://pulse.dreamhorizon.org/docs/sdk/android/instrumentation/custom-spans) - Custom spans and performance tracing
42
+ - [Custom Events](https://pulse.dreamhorizon.org/docs/sdk/react-native/instrumentation/custom-events) - Track business events
43
+ - [Error Boundaries](https://pulse.dreamhorizon.org/docs/sdk/react-native/instrumentation/error-boundaries) - React Error Boundary integration
653
44
 
654
- | Method | Parameters | Returns | Description |
655
- |--------|------------|---------|-------------|
656
- | `Pulse.trackEvent(event, attributes?)` | `event: string`<br/>`attributes?: PulseAttributes` | `void` | Track a custom event with optional attributes. |
45
+ ### Configuration
46
+ - [Global Attributes](https://pulse.dreamhorizon.org/docs/sdk/react-native/global-attributes) - Set attributes for all telemetry
47
+ - [User Identification](https://pulse.dreamhorizon.org/docs/sdk/react-native/user-identification) - Associate telemetry with users
657
48
 
658
49
  ---
659
50
 
660
- ### Performance Tracing
661
-
662
- | Method | Parameters | Returns | Description |
663
- |--------|------------|---------|-------------|
664
- | `Pulse.trackSpan(name, options, fn)` | `name: string`<br/>`options: { attributes?: PulseAttributes }`<br/>`fn: () => T \| Promise<T>` | `T \| Promise<T>` | Auto-managed span. Returns function result. |
665
- | `Pulse.startSpan(name, options?)` | `name: string`<br/>`options?: { attributes?: PulseAttributes }` | `Span` | Create a span with manual control. |
666
-
667
- **Span Interface:**
668
- ```typescript
669
- interface Span {
670
- spanId: string;
671
- end(statusCode?: SpanStatusCode): void;
672
- addEvent(name: string, attributes?: PulseAttributes): void;
673
- setAttributes(attributes?: PulseAttributes): void;
674
- recordException(error: Error, attributes?: PulseAttributes): void;
675
- }
51
+ <div align="center">
676
52
 
677
- // Span status codes
678
- const SpanStatusCode = {
679
- OK: 'OK',
680
- ERROR: 'ERROR',
681
- UNSET: 'UNSET',
682
- } as const;
683
-
684
- type SpanStatusCode = typeof SpanStatusCode[keyof typeof SpanStatusCode];
685
- ```
686
-
687
- ---
688
-
689
- ### User Identification
690
-
691
- | Method | Parameters | Returns | Description |
692
- |--------|------------|---------|-------------|
693
- | `Pulse.setUserId(id)` | `id: string \| null` | `void` | Set user ID. Pass `null` to clear. |
694
- | `Pulse.setUserProperty(name, value)` | `name: string`<br/>`value: string \| null` | `void` | Set a single user property. |
695
- | `Pulse.setUserProperties(properties)` | `properties: PulseAttributes` | `void` | Set multiple user properties. |
696
-
697
- ---
698
-
699
- ### Global Attributes
700
-
701
- | Method | Parameters | Returns | Description |
702
- |--------|------------|---------|-------------|
703
- | `Pulse.setGlobalAttribute(key, value)` | `key: string`<br/>`value: string` | `void` | Set attribute for all events/spans. Pass empty string to remove. |
704
-
705
- ---
706
-
707
- ### Error Boundary
708
-
709
- | Component/HOC | Props | Description |
710
- |---------------|-------|-------------|
711
- | `<Pulse.ErrorBoundary>` | `fallback?: React.ReactElement \| FallbackRender`<br/>`onError?: (error, componentStack) => void` | Catch React render errors. |
712
- | `Pulse.withErrorBoundary(Component, options)` | `Component: React.ComponentType`<br/>`options: ErrorBoundaryProps` | HOC to wrap component with error boundary. |
713
-
714
- ---
715
-
716
- ### Type Definitions
717
-
718
- ```typescript
719
- // Attribute values - primitives and homogeneous arrays
720
- type PulseAttributeValue =
721
- | string
722
- | number
723
- | boolean
724
- | Array<null | undefined | string>
725
- | Array<null | undefined | number>
726
- | Array<null | undefined | boolean>;
727
-
728
- // Attributes object
729
- type PulseAttributes = Record<string, PulseAttributeValue | undefined>;
730
- ```
731
-
732
- ---
733
-
734
- ## Troubleshooting
735
-
736
- ### ⚠️ Warning: "SDK not initialized"
737
-
738
- **Symptoms:**
739
-
740
- JavaScript console:
741
- ```
742
- ⚠️ [Pulse RN] Events will not be sent - SDK not initialized.
743
- Call Pulse.start() and initialize PulseSDK in MainApplication.kt
744
- ```
745
-
746
- Android logcat:
747
- ```
748
- W/PulseLogger: PulseSDK not initialized. Events will not be tracked...
749
- ```
750
-
751
- **Cause:** Native Android SDK not initialized.
752
-
753
- **Solution:**
754
-
755
- 1. Add to `MainApplication.kt`:
756
- ```kotlin
757
- override fun onCreate() {
758
- super.onCreate()
759
- PulseSDK.INSTANCE.initialize(this)
760
- }
761
- ```
762
-
763
- 2. Verify initialization:
764
- ```typescript
765
- console.log('Pulse ready:', Pulse.isInitialized());
766
- ```
767
-
768
- 3. Clean rebuild:
769
- ```bash
770
- cd android && ./gradlew clean
771
- cd .. && npx react-native run-android
772
- ```
773
-
774
- ### Network Requests Not Tracked
775
-
776
- **Possible causes:**
777
- - `autoInstrumentNetwork: false` in `Pulse.start()`
778
- - Using unsupported HTTP library (only fetch/XHR supported)
779
- - Network module initialization race condition
780
-
781
- **Solution:**
782
- ```typescript
783
- Pulse.start({ autoInstrumentNetwork: true });
784
- ```
785
-
786
- ### Navigation Not Tracked
787
-
788
- **Possible causes:**
789
- - `autoInstrumentNavigation: false` in `Pulse.start()`
790
- - Navigation container not registered
791
- - Using non-React Navigation library
792
-
793
- **Solution:**
794
- ```typescript
795
- Pulse.start({ autoInstrumentNavigation: true });
796
-
797
- const integration = Pulse.createNavigationIntegration();
798
- <NavigationContainer onReady={integration.registerNavigationContainer}>
799
- ```
800
-
801
- ---
802
-
803
- ## Best Practices
804
-
805
- ### 1. Initialize Early
806
-
807
- Initialize the native SDK as early as possible in your app lifecycle:
808
-
809
- ```kotlin
810
- class MainApplication : Application() {
811
- override fun onCreate() {
812
- super.onCreate()
813
- PulseSDK.INSTANCE.initialize(this) // First thing
814
- // ... other initializations
815
- }
816
- }
817
- ```
818
-
819
- ### 2. Set User Context Immediately
820
-
821
- Set user information right after authentication:
822
-
823
- ```typescript
824
- async function handleLogin(credentials) {
825
- const user = await login(credentials);
826
-
827
- // Set user context immediately
828
- Pulse.setUserId(user.id);
829
- Pulse.setUserProperties({
830
- email: user.email,
831
- plan: user.subscription,
832
- signupDate: user.createdAt
833
- });
834
- }
835
- ```
836
-
837
- ### 3. Use Global Attributes for Static Metadata
838
-
839
- Set global attributes for information that doesn't change during the session:
840
-
841
- ```typescript
842
- Pulse.start();
843
-
844
- // Set once at app start
845
- Pulse.setGlobalAttribute('appVersion', DeviceInfo.getVersion());
846
- Pulse.setGlobalAttribute('buildType', __DEV__ ? 'debug' : 'release');
847
- Pulse.setGlobalAttribute('deviceModel', DeviceInfo.getModel());
848
- ```
849
-
850
- ### 4. Prefer `trackSpan()` Over `startSpan()`
851
-
852
- Use automatic span management unless you need fine-grained control:
853
-
854
- ```typescript
855
- // ✅ Preferred - automatic lifecycle
856
- await Pulse.trackSpan('operation', {}, async () => {
857
- await doWork();
858
- });
859
-
860
- // ⚠️ Use only when needed - manual lifecycle
861
- const span = Pulse.startSpan('operation');
862
- await doWork();
863
- span.end(); // Easy to forget!
864
- ```
865
-
866
- ### 5. Add Context to Errors
867
-
868
- Always include relevant context when reporting errors:
869
-
870
- ```typescript
871
- try {
872
- await syncData();
873
- } catch (error) {
874
- Pulse.reportException(error, false, {
875
- operation: 'data_sync',
876
- userId: currentUser?.id,
877
- lastSyncTime: lastSync.toISOString(),
878
- itemsPending: pendingItems.length
879
- });
880
- }
881
- ```
882
-
883
- ---
884
-
885
- ## Requirements
886
-
887
- - **React Native:** ≥ 0.70
888
- - **Android:** minSdk ≥ 24, compileSdk ≥ 35
889
- - **Dependencies:**
890
- - [Pulse Android SDK](../pulse-android-otel)
891
-
892
- ---
893
-
894
- ## License
895
-
896
- MIT
897
-
898
- ---
53
+ **Built with ❤️ by the Pulse Team**
899
54
 
900
- Made with ❤️ using [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
55
+ </div>