@dreamhorizonorg/pulse-react-native 0.0.1 → 0.0.25
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.
- package/PulseReactNativeOtel.podspec +1 -1
- package/README.md +36 -875
- package/android/build.gradle +1 -1
- package/lib/module/errorBoundary.js.map +1 -1
- package/lib/module/network-interceptor/request-tracker-xhr.js +1 -0
- package/lib/module/network-interceptor/request-tracker-xhr.js.map +1 -1
- package/lib/module/network-interceptor/span-helpers.js +1 -1
- package/lib/typescript/src/errorBoundary.d.ts.map +1 -1
- package/lib/typescript/src/network-interceptor/request-tracker-xhr.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/errorBoundary.tsx +11 -5
- package/src/global.d.ts +0 -1
- package/src/network-interceptor/request-tracker-xhr.ts +2 -0
- package/src/network-interceptor/span-helpers.ts +1 -1
- package/LICENSE +0 -20
|
@@ -11,7 +11,7 @@ Pod::Spec.new do |s|
|
|
|
11
11
|
s.authors = package["author"]
|
|
12
12
|
|
|
13
13
|
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
-
s.source = { :git => "https://github.com/
|
|
14
|
+
s.source = { :git => "https://github.com/dream-horizon-org/pulse.git", :tag => "#{s.version}" }
|
|
15
15
|
|
|
16
16
|
s.source_files = "ios/**/*.{h,m,mm,cpp}"
|
|
17
17
|
s.private_header_files = "ios/**/*.h"
|
package/README.md
CHANGED
|
@@ -1,900 +1,61 @@
|
|
|
1
|
-
<
|
|
2
|
-
<h1 align="center">Pulse React Native SDK</h1>
|
|
3
|
-
</p>
|
|
1
|
+
<div align="center">
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
<strong>Production-grade observability for React Native applications</strong>
|
|
7
|
-
</p>
|
|
3
|
+
# Pulse React Native SDK
|
|
8
4
|
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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');
|
|
9
|
+
[](https://www.npmjs.com/package/@dreamhorizonorg/pulse-react-native)
|
|
10
|
+
[](https://www.npmjs.com/package/@dreamhorizonorg/pulse-react-native)
|
|
11
|
+
[](../LICENSE)
|
|
416
12
|
|
|
417
|
-
|
|
418
|
-
Pulse.trackEvent('test', { environment: 'staging' });
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
**Common use cases:** CodePush labels, build numbers, environment flags, feature flags, user segments, device metadata.
|
|
422
|
-
|
|
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
|
-
##
|
|
625
|
-
|
|
626
|
-
### Initialization
|
|
627
|
-
|
|
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. |
|
|
17
|
+
## ✨ Key Features
|
|
632
18
|
|
|
633
|
-
**
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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
|
-
|
|
29
|
+
## 📚 Documentation
|
|
645
30
|
|
|
646
|
-
|
|
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://dreamhorizon.org/docs/sdk/react-native/overview)**.
|
|
649
32
|
|
|
650
|
-
|
|
33
|
+
### Getting Started
|
|
34
|
+
- [Overview](https://dreamhorizon.org/docs/sdk/react-native/overview) - Introduction and key features
|
|
35
|
+
- [Quick Start](https://dreamhorizon.org/docs/sdk/react-native/quick-start) - Installation and setup guide
|
|
651
36
|
|
|
652
|
-
###
|
|
37
|
+
### Instrumentation
|
|
38
|
+
- [Network Instrumentation](https://dreamhorizon.org/docs/sdk/react-native/instrumentation/network-instrumentation) - Auto-instrument HTTP requests
|
|
39
|
+
- [Error Instrumentation](https://dreamhorizon.org/docs/sdk/react-native/instrumentation/error-instrumentation) - Error tracking and reporting
|
|
40
|
+
- [Navigation Instrumentation](https://dreamhorizon.org/docs/sdk/react-native/instrumentation/navigation-instrumentation) - React Navigation integration
|
|
41
|
+
- [Custom Instrumentation](https://dreamhorizon.org/docs/sdk/react-native/instrumentation/custom-instrumentation) - Custom spans and performance tracing
|
|
42
|
+
- [Custom Events](https://dreamhorizon.org/docs/sdk/react-native/instrumentation/custom-events) - Track business events
|
|
43
|
+
- [Error Boundaries](https://dreamhorizon.org/docs/sdk/react-native/instrumentation/error-boundaries) - React Error Boundary integration
|
|
653
44
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
45
|
+
### Configuration
|
|
46
|
+
- [Global Attributes](https://dreamhorizon.org/docs/sdk/react-native/global-attributes) - Set attributes for all telemetry
|
|
47
|
+
- [User Identification](https://dreamhorizon.org/docs/sdk/react-native/user-identification) - Associate telemetry with users
|
|
657
48
|
|
|
658
49
|
---
|
|
659
50
|
|
|
660
|
-
|
|
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
|
-
}
|
|
676
|
-
|
|
677
|
-
// Span status codes
|
|
678
|
-
const SpanStatusCode = {
|
|
679
|
-
OK: 'OK',
|
|
680
|
-
ERROR: 'ERROR',
|
|
681
|
-
UNSET: 'UNSET',
|
|
682
|
-
} as const;
|
|
51
|
+
## 🤝 Contributing
|
|
683
52
|
|
|
684
|
-
|
|
685
|
-
```
|
|
53
|
+
We welcome contributions! Please see our [Contributing Guide](https://dreamhorizon.org/docs/contribution) for detailed information.
|
|
686
54
|
|
|
687
55
|
---
|
|
688
56
|
|
|
689
|
-
|
|
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
|
-
---
|
|
57
|
+
<div align="center">
|
|
698
58
|
|
|
699
|
-
|
|
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
|
-
---
|
|
59
|
+
**Built with ❤️ by the Pulse Team**
|
|
899
60
|
|
|
900
|
-
|
|
61
|
+
</div>
|
package/android/build.gradle
CHANGED
|
@@ -79,7 +79,7 @@ dependencies {
|
|
|
79
79
|
implementation("com.facebook.react:react-android")
|
|
80
80
|
implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
|
|
81
81
|
|
|
82
|
-
api("org.dreamhorizon:pulse-android-sdk:0.0.
|
|
82
|
+
api("org.dreamhorizon:pulse-android-sdk:0.0.20-alpha-SNAPSHOT") {
|
|
83
83
|
exclude group: "com.squareup.okhttp3", module: "okhttp-jvm"
|
|
84
84
|
}
|
|
85
85
|
api("com.squareup.okhttp3:okhttp:5.0.0-alpha.14")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","Pulse","jsx","_jsx","UNKNOWN_COMPONENT","COMPONENT_STACK_UNAVAILABLE","INITIAL_STATE","componentStack","error","ErrorBoundary","Component","state","componentDidCatch","errorInfo","onError","props","handled","fallback","errorToReport","Error","String","reportException","setState","render","children","element","createElement","isValidElement","withErrorBoundary","WrappedComponent","errorBoundaryOptions","componentDisplayName","displayName","name","Wrapped","memo"],"sourceRoot":"../../src","sources":["errorBoundary.tsx"],"mappings":";;AAAA,OAAO,KAAKA,KAAK,MAAM,OAAO;AAC9B,SAASC,KAAK,QAAQ,YAAS;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAEhC,OAAO,MAAMC,iBAAiB,GAAG,SAAS;AAC1C,MAAMC,2BAA2B,GAAG,+BAA+B;AAuBnE,MAAMC,aAAiC,GAAG;EACxCC,cAAc,EAAE,IAAI;EACpBC,KAAK,EAAE;AACT,CAAC;AAED,OAAO,MAAMC,aAAa,SAAST,KAAK,CAACU,SAAS,
|
|
1
|
+
{"version":3,"names":["React","Pulse","jsx","_jsx","UNKNOWN_COMPONENT","COMPONENT_STACK_UNAVAILABLE","INITIAL_STATE","componentStack","error","ErrorBoundary","Component","state","componentDidCatch","errorInfo","onError","props","handled","fallback","errorToReport","Error","String","reportException","setState","render","children","element","createElement","isValidElement","withErrorBoundary","WrappedComponent","errorBoundaryOptions","componentDisplayName","displayName","name","Wrapped","memo"],"sourceRoot":"../../src","sources":["errorBoundary.tsx"],"mappings":";;AAAA,OAAO,KAAKA,KAAK,MAAM,OAAO;AAC9B,SAASC,KAAK,QAAQ,YAAS;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAEhC,OAAO,MAAMC,iBAAiB,GAAG,SAAS;AAC1C,MAAMC,2BAA2B,GAAG,+BAA+B;AAuBnE,MAAMC,aAAiC,GAAG;EACxCC,cAAc,EAAE,IAAI;EACpBC,KAAK,EAAE;AACT,CAAC;AAED,OAAO,MAAMC,aAAa,SAAST,KAAK,CAACU,SAAS,CAGhD;EACOC,KAAK,GAAuBL,aAAa;EAEzCM,iBAAiBA,CAACJ,KAAc,EAAEK,SAA0B,EAAQ;IACzE,MAAMN,cAAc,GAClBM,SAAS,CAACN,cAAc,IAAIF,2BAA2B;IACzD,MAAM;MAAES;IAAQ,CAAC,GAAG,IAAI,CAACC,KAAK;;IAE9B;IACA,MAAMC,OAAO,GAAG,CAAC,CAAC,IAAI,CAACD,KAAK,CAACE,QAAQ;IAErC,MAAMC,aAAa,GACjBV,KAAK,YAAYW,KAAK,GAAGX,KAAK,GAAG,IAAIW,KAAK,CAACC,MAAM,CAACZ,KAAK,CAAC,CAAC;IAC3DP,KAAK,CAACoB,eAAe,CAACH,aAAa,EAAE,CAACF,OAAO,CAAC;IAE9C,IAAIF,OAAO,EAAE;MACXA,OAAO,CAACN,KAAK,EAAED,cAAc,CAAC;IAChC;IAEA,IAAI,CAACe,QAAQ,CAAC;MAAEd,KAAK;MAAED;IAAe,CAAC,CAAC;EAC1C;EAEOgB,MAAMA,CAAA,EAAoB;IAC/B,MAAM;MAAEN,QAAQ;MAAEO;IAAS,CAAC,GAAG,IAAI,CAACT,KAAK;IACzC,MAAMJ,KAAK,GAAG,IAAI,CAACA,KAAK;IAExB,IAAIA,KAAK,CAACJ,cAAc,KAAK,IAAI,EAAE;MACjC,OAAO,OAAOiB,QAAQ,KAAK,UAAU,GAAGA,QAAQ,CAAC,CAAC,GAAGA,QAAQ;IAC/D;IAEA,MAAMC,OAAO,GACX,OAAOR,QAAQ,KAAK,UAAU,gBAC1BjB,KAAK,CAAC0B,aAAa,CAACT,QAAQ,EAAE;MAC5BT,KAAK,EAAEG,KAAK,CAACH,KAAK;MAClBD,cAAc,EAAEI,KAAK,CAACJ;IACxB,CAAC,CAAC,GACFU,QAAQ;IAEd,iBAAIjB,KAAK,CAAC2B,cAAc,CAACF,OAAO,CAAC,EAAE;MACjC,OAAOA,OAAO;IAChB;IAEA,OAAO,IAAI;EACb;AACF;AAEA,OAAO,SAASG,iBAAiBA,CAC/BC,gBAAwC,EACxCC,oBAAwC,EAC3B;EACb,MAAMC,oBAAoB,GACxBF,gBAAgB,CAACG,WAAW,IAAIH,gBAAgB,CAACI,IAAI,IAAI7B,iBAAiB;EAE5E,MAAM8B,OAAO,gBAAGlC,KAAK,CAACmC,IAAI,CAAEpB,KAAQ,iBAClCZ,IAAA,CAACM,aAAa;IAAA,GAAKqB,oBAAoB;IAAAN,QAAA,eACrCrB,IAAA,CAAC0B,gBAAgB;MAAA,GAAKd;IAAK,CAAG;EAAC,CAClB,CAChB,CAA2B;EAE5BmB,OAAO,CAACF,WAAW,GAAG,iBAAiBD,oBAAoB,GAAG;EAE9D,OAAOG,OAAO;AAChB","ignoreList":[]}
|
|
@@ -35,6 +35,7 @@ function createXmlHttpRequestTracker(xhr) {
|
|
|
35
35
|
method: requestData.method,
|
|
36
36
|
url: requestData.url
|
|
37
37
|
};
|
|
38
|
+
this.setRequestHeader('X-Pulse-RN-Tracked', 'true');
|
|
38
39
|
const span = createNetworkSpan(startContext, 'xmlhttprequest');
|
|
39
40
|
trackedSpans.set(this, span);
|
|
40
41
|
const {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["RequestTracker","getAbsoluteUrl","createNetworkSpan","completeNetworkSpan","isXHRIntercepted","createXmlHttpRequestTracker","xhr","console","warn","requestTracker","trackedRequests","WeakMap","trackedSpans","requestHandlers","originalOpen","prototype","open","method","url","rest","set","String","call","originalSend","send","body","requestData","get","existingHandler","removeEventListener","startContext","type","span","onRequestEnd","start","onReadyStateChange","readyState","DONE","activeSpan","endContext","status","state","delete","addEventListener"],"sourceRoot":"../../../src","sources":["network-interceptor/request-tracker-xhr.ts"],"mappings":";;AAIA,SAASA,cAAc,QAAQ,sBAAmB;AAClD,SAASC,cAAc,QAAQ,eAAY;AAE3C,SAASC,iBAAiB,EAAEC,mBAAmB,QAAQ,mBAAgB;AASvE,IAAIC,gBAAgB,GAAG,KAAK;AAE5B,SAASC,2BAA2BA,CAClCC,GAA0B,EACV;EAChB,IAAIF,gBAAgB,EAAE;IACpBG,OAAO,CAACC,IAAI,CAAC,4CAA4C,CAAC;IAC1D,OAAO,IAAIR,cAAc,CAAC,CAAC;EAC7B;EAEA,MAAMS,cAAc,GAAG,IAAIT,cAAc,CAAC,CAAC;EAC3C,MAAMU,eAAe,GAAG,IAAIC,OAAO,CAA8B,CAAC;EAClE,MAAMC,YAAY,GAAG,IAAID,OAAO,CAAuB,CAAC;EACxD,MAAME,eAAe,GAAG,IAAIF,OAAO,CAGjC,CAAC;EAEH,MAAMG,YAAY,GAAGR,GAAG,CAACS,SAAS,CAACC,IAAI;EACvCV,GAAG,CAACS,SAAS,CAACC,IAAI,GAAG,SAASA,IAAIA,CAChCC,MAAc,EACdC,GAAiB,EACjB,GAAGC,IAAW,EACR;IACNT,eAAe,CAACU,GAAG,CAAC,IAAI,EAAE;MACxBH,MAAM;MACNC,GAAG,EAAEjB,cAAc,CAACoB,MAAM,CAACH,GAAG,CAAC;IACjC,CAAC,CAAC;;IAEF;IACAJ,YAAY,CAACQ,IAAI,CAAC,IAAI,EAAEL,MAAM,EAAEC,GAAG,EAAE,GAAGC,IAAI,CAAC;EAC/C,CAAC;EACDf,gBAAgB,GAAG,IAAI;EAEvB,MAAMmB,YAAY,GAAGjB,GAAG,CAACS,SAAS,CAACS,IAAI;EACvClB,GAAG,CAACS,SAAS,CAACS,IAAI,GAAG,SAASA,IAAIA,CAChCC,IAA+C,EAC/C;IACA,MAAMC,WAAW,GAAGhB,eAAe,CAACiB,GAAG,CAAC,IAAI,CAAC;IAC7C,IAAID,WAAW,EAAE;MACf,MAAME,eAAe,GAAGf,eAAe,CAACc,GAAG,CAAC,IAAI,CAAC;MACjD,IAAIC,eAAe,EACjB,IAAI,CAACC,mBAAmB,CAAC,kBAAkB,EAAED,eAAe,CAAC;MAE/D,MAAME,YAAiC,GAAG;QACxCC,IAAI,EAAE,gBAAgB;QACtBd,MAAM,EAAES,WAAW,CAACT,MAAM;QAC1BC,GAAG,EAAEQ,WAAW,CAACR;MACnB,CAAC;MAED,
|
|
1
|
+
{"version":3,"names":["RequestTracker","getAbsoluteUrl","createNetworkSpan","completeNetworkSpan","isXHRIntercepted","createXmlHttpRequestTracker","xhr","console","warn","requestTracker","trackedRequests","WeakMap","trackedSpans","requestHandlers","originalOpen","prototype","open","method","url","rest","set","String","call","originalSend","send","body","requestData","get","existingHandler","removeEventListener","startContext","type","setRequestHeader","span","onRequestEnd","start","onReadyStateChange","readyState","DONE","activeSpan","endContext","status","state","delete","addEventListener"],"sourceRoot":"../../../src","sources":["network-interceptor/request-tracker-xhr.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -13,7 +13,7 @@ export function setNetworkSpanAttributes(span, startContext, endContext) {
|
|
|
13
13
|
'platform': Platform.OS
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
// We had implemented our own URL parsing helper to avoid errors on RN < 0.80. Since this is not supported by React Native.
|
|
16
|
+
// We had implemented our own URL parsing helper to avoid errors on RN < 0.80. Since this is not supported by React Native.
|
|
17
17
|
// Check here: https://github.com/facebook/react-native/blob/v0.79.0/packages/react-native/Libraries/Blob/URL.js
|
|
18
18
|
const urlAttributes = extractHttpAttributes(startContext.url);
|
|
19
19
|
attributes = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorBoundary.d.ts","sourceRoot":"","sources":["../../../src/errorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAG3C,MAAM,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE;IACvC,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;CACxB,KAAK,KAAK,CAAC,YAAY,CAAC;AAEzB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC;IACrD,QAAQ,CAAC,EAAE,KAAK,CAAC,YAAY,GAAG,cAAc,GAAG,SAAS,CAAC;IAC3D,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAC1E,CAAC;AAEF,KAAK,kBAAkB,GACnB;IACE,cAAc,EAAE,IAAI,CAAC;IACrB,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAON,qBAAa,aAAc,SAAQ,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"errorBoundary.d.ts","sourceRoot":"","sources":["../../../src/errorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAG3C,MAAM,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE;IACvC,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;CACxB,KAAK,KAAK,CAAC,YAAY,CAAC;AAEzB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,CAAC,MAAM,KAAK,CAAC,SAAS,CAAC,CAAC;IACrD,QAAQ,CAAC,EAAE,KAAK,CAAC,YAAY,GAAG,cAAc,GAAG,SAAS,CAAC;IAC3D,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAC1E,CAAC;AAEF,KAAK,kBAAkB,GACnB;IACE,cAAc,EAAE,IAAI,CAAC;IACrB,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAON,qBAAa,aAAc,SAAQ,KAAK,CAAC,SAAS,CAChD,kBAAkB,EAClB,kBAAkB,CACnB;IACQ,KAAK,EAAE,kBAAkB,CAAiB;IAE1C,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI;IAmBnE,MAAM,IAAI,KAAK,CAAC,SAAS;CAsBjC;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC7D,gBAAgB,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EACxC,oBAAoB,EAAE,kBAAkB,GACvC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAab"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-tracker-xhr.d.ts","sourceRoot":"","sources":["../../../../src/network-interceptor/request-tracker-xhr.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAcnD,iBAAS,2BAA2B,CAClC,GAAG,EAAE,OAAO,cAAc,GACzB,cAAc,
|
|
1
|
+
{"version":3,"file":"request-tracker-xhr.d.ts","sourceRoot":"","sources":["../../../../src/network-interceptor/request-tracker-xhr.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAcnD,iBAAS,2BAA2B,CAClC,GAAG,EAAE,OAAO,cAAc,GACzB,cAAc,CAuFhB;AAED,eAAe,2BAA2B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dreamhorizonorg/pulse-react-native",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25",
|
|
4
4
|
"description": "Production-grade observability SDK for React Native applications with OpenTelemetry support. Real-time monitoring, error tracking, performance insights, and distributed tracing.",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
package/src/errorBoundary.tsx
CHANGED
|
@@ -30,17 +30,22 @@ const INITIAL_STATE: ErrorBoundaryState = {
|
|
|
30
30
|
error: null,
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
export class ErrorBoundary extends React.Component<
|
|
33
|
+
export class ErrorBoundary extends React.Component<
|
|
34
|
+
ErrorBoundaryProps,
|
|
35
|
+
ErrorBoundaryState
|
|
36
|
+
> {
|
|
34
37
|
public state: ErrorBoundaryState = INITIAL_STATE;
|
|
35
38
|
|
|
36
39
|
public componentDidCatch(error: unknown, errorInfo: React.ErrorInfo): void {
|
|
37
|
-
const componentStack =
|
|
40
|
+
const componentStack =
|
|
41
|
+
errorInfo.componentStack || COMPONENT_STACK_UNAVAILABLE;
|
|
38
42
|
const { onError } = this.props;
|
|
39
43
|
|
|
40
44
|
// Error is handled if a fallback is provided, otherwise it's unhandled (fatal)
|
|
41
45
|
const handled = !!this.props.fallback;
|
|
42
46
|
|
|
43
|
-
const errorToReport =
|
|
47
|
+
const errorToReport =
|
|
48
|
+
error instanceof Error ? error : new Error(String(error));
|
|
44
49
|
Pulse.reportException(errorToReport, !handled);
|
|
45
50
|
|
|
46
51
|
if (onError) {
|
|
@@ -76,9 +81,10 @@ export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoun
|
|
|
76
81
|
|
|
77
82
|
export function withErrorBoundary<P extends Record<string, any>>(
|
|
78
83
|
WrappedComponent: React.ComponentType<P>,
|
|
79
|
-
errorBoundaryOptions: ErrorBoundaryProps
|
|
84
|
+
errorBoundaryOptions: ErrorBoundaryProps
|
|
80
85
|
): React.FC<P> {
|
|
81
|
-
const componentDisplayName =
|
|
86
|
+
const componentDisplayName =
|
|
87
|
+
WrappedComponent.displayName || WrappedComponent.name || UNKNOWN_COMPONENT;
|
|
82
88
|
|
|
83
89
|
const Wrapped = React.memo((props: P) => (
|
|
84
90
|
<ErrorBoundary {...errorBoundaryOptions}>
|
package/src/global.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ function createXmlHttpRequestTracker(
|
|
|
64
64
|
url: requestData.url,
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
this.setRequestHeader('X-Pulse-RN-Tracked', 'true');
|
|
68
|
+
|
|
67
69
|
const span = createNetworkSpan(startContext, 'xmlhttprequest');
|
|
68
70
|
trackedSpans.set(this, span);
|
|
69
71
|
const { onRequestEnd } = requestTracker.start(startContext);
|
|
@@ -22,7 +22,7 @@ export function setNetworkSpanAttributes(
|
|
|
22
22
|
'platform': Platform.OS as 'android' | 'ios' | 'web',
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
// We had implemented our own URL parsing helper to avoid errors on RN < 0.80. Since this is not supported by React Native.
|
|
25
|
+
// We had implemented our own URL parsing helper to avoid errors on RN < 0.80. Since this is not supported by React Native.
|
|
26
26
|
// Check here: https://github.com/facebook/react-native/blob/v0.79.0/packages/react-native/Libraries/Blob/URL.js
|
|
27
27
|
const urlAttributes = extractHttpAttributes(startContext.url);
|
|
28
28
|
attributes = { ...attributes, ...urlAttributes };
|
package/LICENSE
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 HorizonOS
|
|
4
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
-
in the Software without restriction, including without limitation the rights
|
|
7
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
-
furnished to do so, subject to the following conditions:
|
|
10
|
-
|
|
11
|
-
The above copyright notice and this permission notice shall be included in all
|
|
12
|
-
copies or substantial portions of the Software.
|
|
13
|
-
|
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
-
SOFTWARE.
|