@error-explorer/react 1.1.1
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/README.md +465 -0
- package/dist/index.cjs +424 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +458 -0
- package/dist/index.d.ts +458 -0
- package/dist/index.js +376 -0
- package/dist/index.js.map +1 -0
- package/dist/router.cjs +125 -0
- package/dist/router.cjs.map +1 -0
- package/dist/router.d.cts +106 -0
- package/dist/router.d.ts +106 -0
- package/dist/router.js +98 -0
- package/dist/router.js.map +1 -0
- package/package.json +85 -0
package/README.md
ADDED
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
# @error-explorer/react
|
|
2
|
+
|
|
3
|
+
Official Error Explorer SDK for React. Provides React-specific error handling with ErrorBoundary, hooks, and React Router integration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🛡️ **ErrorBoundary** - Catch and report React component errors
|
|
8
|
+
- 🪝 **React Hooks** - `useErrorExplorer`, `useErrorHandler`, `useUserContext`
|
|
9
|
+
- 🔄 **React Router integration** - Automatic navigation breadcrumbs
|
|
10
|
+
- 📦 **HOC Support** - `withErrorBoundary` for wrapping components
|
|
11
|
+
- 🎯 **Context Provider** - Share SDK instance across your app
|
|
12
|
+
- 📝 **TypeScript** - Full type definitions included
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# npm
|
|
18
|
+
npm install @error-explorer/react
|
|
19
|
+
|
|
20
|
+
# yarn
|
|
21
|
+
yarn add @error-explorer/react
|
|
22
|
+
|
|
23
|
+
# pnpm
|
|
24
|
+
pnpm add @error-explorer/react
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### Option 1: Provider (Recommended)
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { ErrorExplorerProvider, ErrorBoundary } from '@error-explorer/react';
|
|
33
|
+
|
|
34
|
+
function App() {
|
|
35
|
+
return (
|
|
36
|
+
<ErrorExplorerProvider
|
|
37
|
+
options={{
|
|
38
|
+
token: 'ee_your_project_token',
|
|
39
|
+
project: 'my-react-app',
|
|
40
|
+
environment: 'production',
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
<ErrorBoundary>
|
|
44
|
+
<MainContent />
|
|
45
|
+
</ErrorBoundary>
|
|
46
|
+
</ErrorExplorerProvider>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Option 2: Direct Initialization
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { initErrorExplorer, ErrorBoundary } from '@error-explorer/react';
|
|
55
|
+
|
|
56
|
+
// In your entry file (main.tsx or index.tsx)
|
|
57
|
+
initErrorExplorer({
|
|
58
|
+
token: 'ee_your_project_token',
|
|
59
|
+
project: 'my-react-app',
|
|
60
|
+
environment: 'production',
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
function App() {
|
|
64
|
+
return (
|
|
65
|
+
<ErrorBoundary>
|
|
66
|
+
<MainContent />
|
|
67
|
+
</ErrorBoundary>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## ErrorBoundary
|
|
73
|
+
|
|
74
|
+
Catch errors in component trees and report them to Error Explorer:
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import { ErrorBoundary } from '@error-explorer/react';
|
|
78
|
+
|
|
79
|
+
// Basic usage
|
|
80
|
+
<ErrorBoundary>
|
|
81
|
+
<RiskyComponent />
|
|
82
|
+
</ErrorBoundary>
|
|
83
|
+
|
|
84
|
+
// With custom fallback
|
|
85
|
+
<ErrorBoundary
|
|
86
|
+
fallback={<div>Something went wrong</div>}
|
|
87
|
+
>
|
|
88
|
+
<RiskyComponent />
|
|
89
|
+
</ErrorBoundary>
|
|
90
|
+
|
|
91
|
+
// With render prop fallback
|
|
92
|
+
<ErrorBoundary
|
|
93
|
+
fallback={({ error, resetErrorBoundary }) => (
|
|
94
|
+
<div>
|
|
95
|
+
<p>Error: {error.message}</p>
|
|
96
|
+
<button onClick={resetErrorBoundary}>Try again</button>
|
|
97
|
+
</div>
|
|
98
|
+
)}
|
|
99
|
+
>
|
|
100
|
+
<RiskyComponent />
|
|
101
|
+
</ErrorBoundary>
|
|
102
|
+
|
|
103
|
+
// With callbacks and context
|
|
104
|
+
<ErrorBoundary
|
|
105
|
+
onError={(error, errorInfo) => console.log('Caught:', error)}
|
|
106
|
+
onReset={() => console.log('Reset')}
|
|
107
|
+
tags={{ component: 'UserProfile' }}
|
|
108
|
+
context={{ userId: '123' }}
|
|
109
|
+
>
|
|
110
|
+
<UserProfile />
|
|
111
|
+
</ErrorBoundary>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### ErrorBoundary Props
|
|
115
|
+
|
|
116
|
+
| Prop | Type | Description |
|
|
117
|
+
|------|------|-------------|
|
|
118
|
+
| `children` | ReactNode | Components to wrap |
|
|
119
|
+
| `fallback` | ReactNode \| Function | UI to show on error |
|
|
120
|
+
| `onError` | Function | Called when error is caught |
|
|
121
|
+
| `onReset` | Function | Called when boundary resets |
|
|
122
|
+
| `capture` | boolean | Send to Error Explorer (default: true) |
|
|
123
|
+
| `tags` | Record<string, string> | Additional tags for filtering |
|
|
124
|
+
| `context` | Record<string, unknown> | Extra context data |
|
|
125
|
+
| `resetKeys` | unknown[] | Keys that trigger reset when changed |
|
|
126
|
+
|
|
127
|
+
## withErrorBoundary HOC
|
|
128
|
+
|
|
129
|
+
Wrap components with error boundary using HOC pattern:
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
import { withErrorBoundary } from '@error-explorer/react';
|
|
133
|
+
|
|
134
|
+
const SafeComponent = withErrorBoundary(RiskyComponent, {
|
|
135
|
+
fallback: <ErrorFallback />,
|
|
136
|
+
onError: (error) => console.error(error),
|
|
137
|
+
tags: { feature: 'checkout' },
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Use it like a normal component
|
|
141
|
+
<SafeComponent userId="123" />
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Hooks
|
|
145
|
+
|
|
146
|
+
### useErrorExplorer
|
|
147
|
+
|
|
148
|
+
Access SDK methods from any component:
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import { useErrorExplorer } from '@error-explorer/react';
|
|
152
|
+
|
|
153
|
+
function MyComponent() {
|
|
154
|
+
const { captureException, addBreadcrumb, setUser } = useErrorExplorer();
|
|
155
|
+
|
|
156
|
+
const handleClick = async () => {
|
|
157
|
+
try {
|
|
158
|
+
await riskyOperation();
|
|
159
|
+
} catch (error) {
|
|
160
|
+
captureException(error, {
|
|
161
|
+
tags: { action: 'risky_operation' },
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
return <button onClick={handleClick}>Do Something</button>;
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### useErrorHandler
|
|
171
|
+
|
|
172
|
+
Simplified error handling for async operations:
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
import { useErrorHandler } from '@error-explorer/react';
|
|
176
|
+
|
|
177
|
+
function MyComponent() {
|
|
178
|
+
const { handleError, wrapAsync, tryCatch } = useErrorHandler({
|
|
179
|
+
tags: { component: 'MyComponent' },
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Option 1: Wrap async function
|
|
183
|
+
const safeSubmit = wrapAsync(async (data) => {
|
|
184
|
+
await api.submit(data);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Option 2: Manual handling
|
|
188
|
+
const handleClick = async () => {
|
|
189
|
+
try {
|
|
190
|
+
await riskyOperation();
|
|
191
|
+
} catch (error) {
|
|
192
|
+
handleError(error, { extra: { action: 'click' } });
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// Option 3: Sync try-catch wrapper
|
|
197
|
+
const result = tryCatch(() => JSON.parse(data));
|
|
198
|
+
|
|
199
|
+
return <button onClick={() => safeSubmit({ name: 'test' })}>Submit</button>;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### useUserContext
|
|
204
|
+
|
|
205
|
+
Manage user context with automatic cleanup:
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
import { useUserContext } from '@error-explorer/react';
|
|
209
|
+
|
|
210
|
+
function App() {
|
|
211
|
+
const currentUser = useAuth(); // Your auth hook
|
|
212
|
+
|
|
213
|
+
// Automatically sets user context and clears on logout/unmount
|
|
214
|
+
useUserContext(currentUser ? {
|
|
215
|
+
id: currentUser.id,
|
|
216
|
+
email: currentUser.email,
|
|
217
|
+
name: currentUser.name,
|
|
218
|
+
plan: currentUser.plan, // Custom fields allowed
|
|
219
|
+
} : null);
|
|
220
|
+
|
|
221
|
+
return <MainContent />;
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### useActionTracker
|
|
226
|
+
|
|
227
|
+
Track user interactions as breadcrumbs:
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
import { useActionTracker } from '@error-explorer/react';
|
|
231
|
+
|
|
232
|
+
function CheckoutForm() {
|
|
233
|
+
const { trackAction, trackInteraction } = useActionTracker('CheckoutForm');
|
|
234
|
+
|
|
235
|
+
const handleSubmit = () => {
|
|
236
|
+
trackAction('checkout_submitted', { total: 149.99 });
|
|
237
|
+
// ... submit logic
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<form onSubmit={handleSubmit}>
|
|
242
|
+
<input
|
|
243
|
+
onFocus={() => trackInteraction('email-input', 'focus')}
|
|
244
|
+
onBlur={() => trackInteraction('email-input', 'blur')}
|
|
245
|
+
/>
|
|
246
|
+
<button onClick={() => trackInteraction('submit-button', 'click')}>
|
|
247
|
+
Pay Now
|
|
248
|
+
</button>
|
|
249
|
+
</form>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### useComponentBreadcrumbs
|
|
255
|
+
|
|
256
|
+
Track component lifecycle:
|
|
257
|
+
|
|
258
|
+
```tsx
|
|
259
|
+
import { useComponentBreadcrumbs } from '@error-explorer/react';
|
|
260
|
+
|
|
261
|
+
function UserDashboard() {
|
|
262
|
+
// Adds breadcrumbs for mount/unmount
|
|
263
|
+
useComponentBreadcrumbs('UserDashboard');
|
|
264
|
+
|
|
265
|
+
return <div>Dashboard content</div>;
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### useErrorBoundary
|
|
270
|
+
|
|
271
|
+
Programmatically trigger error boundary from functional components:
|
|
272
|
+
|
|
273
|
+
```tsx
|
|
274
|
+
import { ErrorBoundary, useErrorBoundary } from '@error-explorer/react';
|
|
275
|
+
|
|
276
|
+
function AsyncComponent() {
|
|
277
|
+
const { showBoundary } = useErrorBoundary();
|
|
278
|
+
|
|
279
|
+
const loadData = async () => {
|
|
280
|
+
try {
|
|
281
|
+
await fetchData();
|
|
282
|
+
} catch (error) {
|
|
283
|
+
showBoundary(error); // Triggers nearest ErrorBoundary
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
return <button onClick={loadData}>Load Data</button>;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Wrap with ErrorBoundary to catch the error
|
|
291
|
+
<ErrorBoundary>
|
|
292
|
+
<AsyncComponent />
|
|
293
|
+
</ErrorBoundary>
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## React Router Integration
|
|
297
|
+
|
|
298
|
+
Track navigation as breadcrumbs:
|
|
299
|
+
|
|
300
|
+
### Using the Hook (React Router v6+)
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
import { useLocation } from 'react-router-dom';
|
|
304
|
+
import { useRouterBreadcrumbs } from '@error-explorer/react/router';
|
|
305
|
+
|
|
306
|
+
function App() {
|
|
307
|
+
const location = useLocation();
|
|
308
|
+
|
|
309
|
+
// Track navigation automatically
|
|
310
|
+
useRouterBreadcrumbs(location, {
|
|
311
|
+
trackQuery: false, // Don't include query params (default)
|
|
312
|
+
trackHash: false, // Don't include hash (default)
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
return <Routes>{/* your routes */}</Routes>;
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Using the Router Subscriber
|
|
320
|
+
|
|
321
|
+
```tsx
|
|
322
|
+
import { createBrowserRouter } from 'react-router-dom';
|
|
323
|
+
import { createNavigationListener } from '@error-explorer/react/router';
|
|
324
|
+
|
|
325
|
+
const router = createBrowserRouter([/* routes */]);
|
|
326
|
+
|
|
327
|
+
// Subscribe to navigation
|
|
328
|
+
createNavigationListener(router);
|
|
329
|
+
|
|
330
|
+
// In your app
|
|
331
|
+
<RouterProvider router={router} />
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Using HOC
|
|
335
|
+
|
|
336
|
+
```tsx
|
|
337
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
338
|
+
import { withRouterTracking } from '@error-explorer/react/router';
|
|
339
|
+
|
|
340
|
+
const TrackedRouter = withRouterTracking(BrowserRouter, {
|
|
341
|
+
trackQuery: true,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
function App() {
|
|
345
|
+
return (
|
|
346
|
+
<TrackedRouter>
|
|
347
|
+
<Routes>{/* your routes */}</Routes>
|
|
348
|
+
</TrackedRouter>
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## TypeScript Support
|
|
354
|
+
|
|
355
|
+
Full TypeScript support with exported types:
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
import {
|
|
359
|
+
ErrorBoundary,
|
|
360
|
+
ErrorExplorerProvider,
|
|
361
|
+
useErrorExplorer,
|
|
362
|
+
type ReactErrorExplorerOptions,
|
|
363
|
+
type ErrorBoundaryProps,
|
|
364
|
+
type FallbackProps,
|
|
365
|
+
type ErrorExplorerContextValue,
|
|
366
|
+
} from '@error-explorer/react';
|
|
367
|
+
|
|
368
|
+
// All types are fully typed
|
|
369
|
+
const options: ReactErrorExplorerOptions = {
|
|
370
|
+
token: 'ee_token',
|
|
371
|
+
project: 'my-app',
|
|
372
|
+
environment: 'production',
|
|
373
|
+
};
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## Configuration
|
|
377
|
+
|
|
378
|
+
All configuration options from `@error-explorer/browser` are supported, plus React-specific options:
|
|
379
|
+
|
|
380
|
+
```tsx
|
|
381
|
+
<ErrorExplorerProvider
|
|
382
|
+
options={{
|
|
383
|
+
// Required
|
|
384
|
+
token: 'ee_your_project_token',
|
|
385
|
+
project: 'my-react-app',
|
|
386
|
+
|
|
387
|
+
// Recommended
|
|
388
|
+
environment: 'production',
|
|
389
|
+
release: '1.0.0',
|
|
390
|
+
|
|
391
|
+
// React-specific
|
|
392
|
+
captureComponentStack: true, // Include React component stack (default: true)
|
|
393
|
+
|
|
394
|
+
// Filtering
|
|
395
|
+
ignoreErrors: [/ResizeObserver/],
|
|
396
|
+
|
|
397
|
+
// Hooks
|
|
398
|
+
beforeSend: (event) => {
|
|
399
|
+
// Modify or filter events
|
|
400
|
+
return event;
|
|
401
|
+
},
|
|
402
|
+
}}
|
|
403
|
+
>
|
|
404
|
+
<App />
|
|
405
|
+
</ErrorExplorerProvider>
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## Best Practices
|
|
409
|
+
|
|
410
|
+
1. **Wrap at the top level** - Add ErrorBoundary at your app's root
|
|
411
|
+
2. **Use multiple boundaries** - Add ErrorBoundaries around critical sections
|
|
412
|
+
3. **Set user context early** - Use `useUserContext` after authentication
|
|
413
|
+
4. **Track key actions** - Use `useActionTracker` for important user flows
|
|
414
|
+
5. **Use meaningful tags** - Add tags like `feature`, `component`, `action`
|
|
415
|
+
|
|
416
|
+
## Example: Complete Setup
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
// main.tsx
|
|
420
|
+
import React from 'react';
|
|
421
|
+
import ReactDOM from 'react-dom/client';
|
|
422
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
423
|
+
import {
|
|
424
|
+
ErrorExplorerProvider,
|
|
425
|
+
ErrorBoundary,
|
|
426
|
+
} from '@error-explorer/react';
|
|
427
|
+
import App from './App';
|
|
428
|
+
|
|
429
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
430
|
+
<React.StrictMode>
|
|
431
|
+
<ErrorExplorerProvider
|
|
432
|
+
options={{
|
|
433
|
+
token: import.meta.env.VITE_ERROR_EXPLORER_TOKEN,
|
|
434
|
+
project: 'my-react-app',
|
|
435
|
+
environment: import.meta.env.MODE,
|
|
436
|
+
release: import.meta.env.VITE_APP_VERSION,
|
|
437
|
+
}}
|
|
438
|
+
>
|
|
439
|
+
<ErrorBoundary
|
|
440
|
+
fallback={({ error, resetErrorBoundary }) => (
|
|
441
|
+
<div className="error-page">
|
|
442
|
+
<h1>Oops! Something went wrong</h1>
|
|
443
|
+
<p>{error.message}</p>
|
|
444
|
+
<button onClick={resetErrorBoundary}>Try again</button>
|
|
445
|
+
</div>
|
|
446
|
+
)}
|
|
447
|
+
>
|
|
448
|
+
<BrowserRouter>
|
|
449
|
+
<App />
|
|
450
|
+
</BrowserRouter>
|
|
451
|
+
</ErrorBoundary>
|
|
452
|
+
</ErrorExplorerProvider>
|
|
453
|
+
</React.StrictMode>
|
|
454
|
+
);
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
## Related Packages
|
|
458
|
+
|
|
459
|
+
- `@error-explorer/browser` - Core browser SDK
|
|
460
|
+
- `@error-explorer/vue` - Vue.js SDK
|
|
461
|
+
- `@error-explorer/node` - Node.js SDK
|
|
462
|
+
|
|
463
|
+
## License
|
|
464
|
+
|
|
465
|
+
MIT
|