@multiplayer-app/session-recorder-react 1.2.25 → 1.2.27
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 +68 -0
- package/dist/ErrorBoundary.d.ts +17 -0
- package/dist/ErrorBoundary.d.ts.map +1 -0
- package/dist/ErrorBoundary.js +27 -0
- package/dist/ErrorBoundary.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/ErrorBoundary.tsx +38 -0
- package/src/index.ts +1 -1
package/README.md
CHANGED
|
@@ -270,6 +270,74 @@ The options passed to `SessionRecorder.init(...)` are forwarded to the underlyin
|
|
|
270
270
|
|
|
271
271
|
Any time `recordNavigation` is enabled, the browser SDK will emit OpenTelemetry navigation spans and keep an in-memory stack of visited routes. You can access the navigation helpers through `SessionRecorder.navigation` if you need to introspect from React components.
|
|
272
272
|
|
|
273
|
+
## Capturing exceptions in React apps
|
|
274
|
+
|
|
275
|
+
The browser SDK auto‑captures uncaught errors and unhandled promise rejections. In React apps you’ll typically also want an Error Boundary to catch render errors and report them. This package ships a ready‑to‑use boundary and also shows how to wire React 18/19 root error callbacks.
|
|
276
|
+
|
|
277
|
+
### Using the built‑in error boundary
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
import React from 'react'
|
|
281
|
+
import { ErrorBoundary } from '@multiplayer-app/session-recorder-react'
|
|
282
|
+
|
|
283
|
+
export function AppWithBoundary() {
|
|
284
|
+
return (
|
|
285
|
+
<ErrorBoundary fallback={<div>Something went wrong</div>}>
|
|
286
|
+
<App />
|
|
287
|
+
</ErrorBoundary>
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The boundary calls `SessionRecorder.captureException(error)` internally and renders the provided `fallback` on error.
|
|
293
|
+
|
|
294
|
+
### Custom boundary (if you need full control)
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
import React from 'react'
|
|
298
|
+
import SessionRecorder from '@multiplayer-app/session-recorder-react'
|
|
299
|
+
|
|
300
|
+
class MyErrorBoundary extends React.Component<{ children: React.ReactNode }, { hasError: boolean }> {
|
|
301
|
+
state = { hasError: false }
|
|
302
|
+
static getDerivedStateFromError() {
|
|
303
|
+
return { hasError: true }
|
|
304
|
+
}
|
|
305
|
+
componentDidCatch(error: unknown) {
|
|
306
|
+
SessionRecorder.captureException(error as any)
|
|
307
|
+
}
|
|
308
|
+
render() {
|
|
309
|
+
return this.state.hasError ? <h1>Oops.</h1> : this.props.children
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### React 18/19 root error hooks
|
|
315
|
+
|
|
316
|
+
React surfaces recoverable runtime errors via the root option `onRecoverableError`. You can forward these to the session recorder as well. This works in React 18 and React 19.
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
import React from 'react'
|
|
320
|
+
import ReactDOM from 'react-dom/client'
|
|
321
|
+
import SessionRecorder from '@multiplayer-app/session-recorder-react'
|
|
322
|
+
import App from './App'
|
|
323
|
+
|
|
324
|
+
SessionRecorder.init({
|
|
325
|
+
/* ... your config ... */
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
ReactDOM.createRoot(document.getElementById('root')!, {
|
|
329
|
+
onRecoverableError(error) {
|
|
330
|
+
SessionRecorder.captureException(error as any)
|
|
331
|
+
}
|
|
332
|
+
}).render(<App />)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Notes:
|
|
336
|
+
|
|
337
|
+
- Uncaught errors and unhandled rejections are captured automatically by the browser SDK.
|
|
338
|
+
- Error Boundary + `onRecoverableError` covers both render and runtime recoverable errors.
|
|
339
|
+
- In Continuous mode, any captured exception marks the trace as ERROR and auto‑saves the rolling session window.
|
|
340
|
+
|
|
273
341
|
## Next.js integration tips
|
|
274
342
|
|
|
275
343
|
- Initialize the provider in a Client Component (for example `app/providers.tsx`) because the browser SDK requires `window`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type ErrorBoundaryProps = {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
/** Optional fallback UI to render when an error is caught */
|
|
5
|
+
fallback?: React.ReactNode;
|
|
6
|
+
};
|
|
7
|
+
type State = {
|
|
8
|
+
hasError: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare class ErrorBoundary extends React.Component<ErrorBoundaryProps, State> {
|
|
11
|
+
constructor(props: ErrorBoundaryProps);
|
|
12
|
+
static getDerivedStateFromError(): State;
|
|
13
|
+
componentDidCatch(error: unknown, errorInfo: React.ErrorInfo): void;
|
|
14
|
+
render(): React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
export default ErrorBoundary;
|
|
17
|
+
//# sourceMappingURL=ErrorBoundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../src/ErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B,CAAA;AAED,KAAK,KAAK,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAA;AAElC,qBAAa,aAAc,SAAQ,KAAK,CAAC,SAAS,CAAC,kBAAkB,EAAE,KAAK,CAAC;gBAC/D,KAAK,EAAE,kBAAkB;IAKrC,MAAM,CAAC,wBAAwB,IAAI,KAAK;IAIxC,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,GAAG,IAAI;IAQnE,MAAM,IAAI,KAAK,CAAC,SAAS;CAM1B;AAED,eAAe,aAAa,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import SessionRecorder from '@multiplayer-app/session-recorder-browser';
|
|
3
|
+
export class ErrorBoundary extends React.Component {
|
|
4
|
+
constructor(props) {
|
|
5
|
+
super(props);
|
|
6
|
+
this.state = { hasError: false };
|
|
7
|
+
}
|
|
8
|
+
static getDerivedStateFromError() {
|
|
9
|
+
return { hasError: true };
|
|
10
|
+
}
|
|
11
|
+
componentDidCatch(error, errorInfo) {
|
|
12
|
+
try {
|
|
13
|
+
SessionRecorder.captureException(error, errorInfo);
|
|
14
|
+
}
|
|
15
|
+
catch (_e) {
|
|
16
|
+
// no-op
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
render() {
|
|
20
|
+
if (this.state.hasError) {
|
|
21
|
+
return this.props.fallback ?? null;
|
|
22
|
+
}
|
|
23
|
+
return this.props.children;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export default ErrorBoundary;
|
|
27
|
+
//# sourceMappingURL=ErrorBoundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.js","sourceRoot":"","sources":["../src/ErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,eAAe,MAAM,2CAA2C,CAAA;AAUvE,MAAM,OAAO,aAAc,SAAQ,KAAK,CAAC,SAAoC;IAC3E,YAAY,KAAyB;QACnC,KAAK,CAAC,KAAK,CAAC,CAAA;QACZ,IAAI,CAAC,KAAK,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IAClC,CAAC;IAED,MAAM,CAAC,wBAAwB;QAC7B,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;IAC3B,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,SAA0B;QAC1D,IAAI,CAAC;YACH,eAAe,CAAC,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;QACpD,CAAC;QAAC,OAAO,EAAE,EAAE,CAAC;YACZ,QAAQ;QACV,CAAC;IACH,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAA;QACpC,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAA;IAC5B,CAAC;CACF;AAED,eAAe,aAAa,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from '@multiplayer-app/session-recorder-browser';
|
|
|
2
2
|
export { default } from '@multiplayer-app/session-recorder-browser';
|
|
3
3
|
export * from './context/SessionRecorderContext';
|
|
4
4
|
export * from './context/useSessionRecorderStore';
|
|
5
|
+
export { ErrorBoundary } from './ErrorBoundary';
|
|
5
6
|
export { useNavigationRecorder } from './navigation';
|
|
6
7
|
export type { UseNavigationRecorderOptions } from './navigation';
|
|
7
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2CAA2C,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,2CAA2C,CAAC;AAEpE,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2CAA2C,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,2CAA2C,CAAC;AAEpE,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AACpD,YAAY,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -2,5 +2,6 @@ export * from '@multiplayer-app/session-recorder-browser';
|
|
|
2
2
|
export { default } from '@multiplayer-app/session-recorder-browser';
|
|
3
3
|
export * from './context/SessionRecorderContext';
|
|
4
4
|
export * from './context/useSessionRecorderStore';
|
|
5
|
+
export { ErrorBoundary } from './ErrorBoundary';
|
|
5
6
|
export { useNavigationRecorder } from './navigation';
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2CAA2C,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,2CAA2C,CAAC;AAEpE,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2CAA2C,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,2CAA2C,CAAC;AAEpE,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplayer-app/session-recorder-react",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.27",
|
|
4
4
|
"description": "Multiplayer Fullstack Session Recorder for React (browser wrapper)",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Multiplayer Software, Inc.",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"@opentelemetry/api": "^1.9.0"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@multiplayer-app/session-recorder-browser": "1.2.
|
|
39
|
-
"@multiplayer-app/session-recorder-common": "1.2.
|
|
38
|
+
"@multiplayer-app/session-recorder-browser": "1.2.27",
|
|
39
|
+
"@multiplayer-app/session-recorder-common": "1.2.27"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"eslint": "8.48.0",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import SessionRecorder from '@multiplayer-app/session-recorder-browser'
|
|
3
|
+
|
|
4
|
+
export type ErrorBoundaryProps = {
|
|
5
|
+
children: React.ReactNode
|
|
6
|
+
/** Optional fallback UI to render when an error is caught */
|
|
7
|
+
fallback?: React.ReactNode
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type State = { hasError: boolean }
|
|
11
|
+
|
|
12
|
+
export class ErrorBoundary extends React.Component<ErrorBoundaryProps, State> {
|
|
13
|
+
constructor(props: ErrorBoundaryProps) {
|
|
14
|
+
super(props)
|
|
15
|
+
this.state = { hasError: false }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static getDerivedStateFromError(): State {
|
|
19
|
+
return { hasError: true }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
componentDidCatch(error: unknown, errorInfo: React.ErrorInfo): void {
|
|
23
|
+
try {
|
|
24
|
+
SessionRecorder.captureException(error, errorInfo)
|
|
25
|
+
} catch (_e) {
|
|
26
|
+
// no-op
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
render(): React.ReactNode {
|
|
31
|
+
if (this.state.hasError) {
|
|
32
|
+
return this.props.fallback ?? null
|
|
33
|
+
}
|
|
34
|
+
return this.props.children
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default ErrorBoundary
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,6 @@ export { default } from '@multiplayer-app/session-recorder-browser';
|
|
|
3
3
|
|
|
4
4
|
export * from './context/SessionRecorderContext';
|
|
5
5
|
export * from './context/useSessionRecorderStore';
|
|
6
|
-
|
|
6
|
+
export { ErrorBoundary } from './ErrorBoundary'
|
|
7
7
|
export { useNavigationRecorder } from './navigation'
|
|
8
8
|
export type { UseNavigationRecorderOptions } from './navigation'
|