@flareapp/react 1.2.0 → 2.0.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/.oxlintrc.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "../../node_modules/oxlint/configuration_schema.json",
3
+ "extends": ["../../.oxlintrc.json"],
4
+ "plugins": ["react"],
5
+ "env": {
6
+ "browser": true
7
+ }
8
+ }
package/CHANGELOG.md CHANGED
@@ -1,12 +1,17 @@
1
- # @flareapp/react
1
+ ## 2.0.0
2
2
 
3
- ## 1.2.0
3
+ ### Breaking changes
4
4
 
5
- ### Changed
6
- - Peer dependency on `@flareapp/js` tightened from `^1.0.0` to `^1.2.0`. Consumers must upgrade `@flareapp/js` in lock-step so the v2-wire-format mapper is present at the egress boundary.
5
+ - Bumps peer dependency `@flareapp/js` to `^2.0.0`. See its CHANGELOG for wire format changes.
6
+ - `flareReactErrorHandler` no longer passes a third `extraSolutionParameters` arg to `flare.report` (solutions API removed in `@flareapp/js` v2).
7
+ - The React component stack now lands on the report as `attributes['context.custom'].react.componentStack` and `attributes['context.custom'].react.componentStackFrames` (was nested under `context.react.*`).
7
8
 
8
- No functional changes in this package.
9
+ ### New
9
10
 
10
- ## 1.0.1
11
-
12
- Initial release tracked here. Prior history is in git.
11
+ - **`FlareErrorBoundary` lifecycle hooks.** `beforeEvaluate`, `beforeSubmit`, and `afterSubmit` props give full control over the report pipeline from within the boundary.
12
+ - **`resetKeys` prop.** Mirrors `react-error-boundary`'s contract: when any element changes by `Object.is`, the boundary auto-resets. Pairs with the `onReset` callback.
13
+ - **`FlareErrorBoundaryFallbackProps`** now includes `resetErrorBoundary()` for programmatic reset from fallback UI.
14
+ - **`flareReactErrorHandler()` lifecycle hooks.** Same `beforeEvaluate`, `beforeSubmit`, and `afterSubmit` options, for use with third-party error boundaries.
15
+ - Non-`Error` values passed to `flareReactErrorHandler` are coerced to `Error`.
16
+ - The package self-identifies via `flare.setSdkInfo({ name: '@flareapp/react', version })` and `flare.setFramework({ name: 'React', version: React.version })` on import.
17
+ - New exported types: `ComponentStackFrame`, `FlareReactContext`, `FlareReactErrorHandlerCallback`, `FlareReactErrorHandlerOptions`.
package/README.md CHANGED
@@ -1,5 +1,43 @@
1
- # The JavaScript client for Flare to catch frontend errors
1
+ # @flareapp/react
2
2
 
3
- Read the JavaScript error tracking section in [the Flare documentation](https://flareapp.io/docs/javascript-error-tracking/installation) for more information.
3
+ React integration for [Flare](https://flareapp.io) error tracking. Provides an error boundary component and a React 19+
4
+ error handler for catching and reporting React component errors to Flare.
4
5
 
5
- Flare client: https://www.npmjs.com/package/@flareapp/js
6
+ ## Installation
7
+
8
+ ```bash
9
+ npm install @flareapp/react @flareapp/js
10
+ ```
11
+
12
+ ## Quick start
13
+
14
+ Initialize the Flare client and wrap your component tree with the error boundary:
15
+
16
+ ```tsx
17
+ import { flare } from '@flareapp/js';
18
+ import { FlareErrorBoundary } from '@flareapp/react';
19
+
20
+ flare.light('YOUR_FLARE_API_KEY');
21
+
22
+ function App() {
23
+ return (
24
+ <FlareErrorBoundary fallback={<p>Something went wrong.</p>}>
25
+ <MyComponent />
26
+ </FlareErrorBoundary>
27
+ );
28
+ }
29
+ ```
30
+
31
+ ## Documentation
32
+
33
+ Full documentation on the error boundary, the React 19+ error handler, lifecycle callbacks, and more is available
34
+ at [flareapp.io/docs/react/general/installation](https://flareapp.io/docs/react/general/installation).
35
+
36
+ ## Compatibility
37
+
38
+ - React 16, 17, 18, 19
39
+ - `flareReactErrorHandler` requires React 19+
40
+
41
+ ## License
42
+
43
+ The MIT License (MIT). Please see [License File](../../LICENSE.md) for more information.
package/dist/index.cjs ADDED
@@ -0,0 +1,194 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) {
14
+ __defProp(to, key, {
15
+ get: ((k) => from[k]).bind(null, key),
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ });
18
+ }
19
+ }
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
24
+ value: mod,
25
+ enumerable: true
26
+ }) : target, mod));
27
+
28
+ //#endregion
29
+ let _flareapp_js = require("@flareapp/js");
30
+ let react = require("react");
31
+ react = __toESM(react);
32
+
33
+ //#region src/constants.ts
34
+ const CHROMIUM_STACK_REGEX = /^at\s+(\S+)(?:\s+\((.+):(\d+):(\d+)\))?$/;
35
+ const FIREFOX_SAFARI_STACK_REGEX = /^(\S+?)@(.+):(\d+):(\d+)$/;
36
+ const PACKAGE_VERSION = typeof process !== "undefined" && true ? "2.0.1" : "?";
37
+
38
+ //#endregion
39
+ //#region src/identify.ts
40
+ let registered = false;
41
+ function registerReactSdkIdentity() {
42
+ if (registered) return;
43
+ registered = true;
44
+ _flareapp_js.flare.setSdkInfo({
45
+ name: "@flareapp/react",
46
+ version: PACKAGE_VERSION
47
+ });
48
+ _flareapp_js.flare.setFramework({
49
+ name: "React",
50
+ version: react.version
51
+ });
52
+ }
53
+
54
+ //#endregion
55
+ //#region src/contextToAttributes.ts
56
+ function contextToAttributes(context) {
57
+ return { "context.custom": { react: {
58
+ componentStack: context.react.componentStack,
59
+ componentStackFrames: context.react.componentStackFrames
60
+ } } };
61
+ }
62
+
63
+ //#endregion
64
+ //#region src/formatComponentStack.ts
65
+ function formatComponentStack(stack) {
66
+ return stack.split(/\s*\n\s*/g).filter((line) => line.length > 0);
67
+ }
68
+
69
+ //#endregion
70
+ //#region src/parseComponentStack.ts
71
+ function parseComponentStack(stack) {
72
+ return stack.split(/\s*\n\s*/g).filter((line) => line.length > 0).map((line) => {
73
+ const chromeMatch = line.match(CHROMIUM_STACK_REGEX);
74
+ if (chromeMatch) return {
75
+ component: chromeMatch[1],
76
+ file: chromeMatch[2] ?? null,
77
+ line: chromeMatch[3] ? Number(chromeMatch[3]) : null,
78
+ column: chromeMatch[4] ? Number(chromeMatch[4]) : null
79
+ };
80
+ const firefoxSafariMatch = line.match(FIREFOX_SAFARI_STACK_REGEX);
81
+ if (firefoxSafariMatch) return {
82
+ component: firefoxSafariMatch[1],
83
+ file: firefoxSafariMatch[2],
84
+ line: Number(firefoxSafariMatch[3]),
85
+ column: Number(firefoxSafariMatch[4])
86
+ };
87
+ return {
88
+ component: line.replace(/^at\s+/, ""),
89
+ file: null,
90
+ line: null,
91
+ column: null
92
+ };
93
+ });
94
+ }
95
+
96
+ //#endregion
97
+ //#region src/FlareErrorBoundary.ts
98
+ var FlareErrorBoundary = class extends react.Component {
99
+ state = {
100
+ error: null,
101
+ componentStack: []
102
+ };
103
+ static getDerivedStateFromError(error) {
104
+ return { error };
105
+ }
106
+ componentDidCatch(error, errorInfo) {
107
+ this.props.beforeEvaluate?.({
108
+ error,
109
+ errorInfo
110
+ });
111
+ const rawStack = errorInfo.componentStack ?? "";
112
+ const context = { react: {
113
+ componentStack: formatComponentStack(rawStack),
114
+ componentStackFrames: parseComponentStack(rawStack)
115
+ } };
116
+ const finalContext = this.props.beforeSubmit?.({
117
+ error,
118
+ errorInfo,
119
+ context
120
+ }) ?? context;
121
+ this.setState({ componentStack: finalContext.react.componentStack });
122
+ _flareapp_js.flare.reportSilently(error, contextToAttributes(finalContext));
123
+ this.props.afterSubmit?.({
124
+ error,
125
+ errorInfo,
126
+ context: finalContext
127
+ });
128
+ }
129
+ componentDidUpdate(prevProps) {
130
+ if (this.state.error === null || !this.props.resetKeys) return;
131
+ const prevKeys = prevProps.resetKeys;
132
+ const nextKeys = this.props.resetKeys;
133
+ const lengthChanged = prevKeys?.length !== nextKeys.length;
134
+ const valuesChanged = nextKeys.some((key, i) => !Object.is(key, prevKeys?.[i]));
135
+ if (lengthChanged || valuesChanged) this.reset();
136
+ }
137
+ reset = () => {
138
+ const { error } = this.state;
139
+ this.props.onReset?.(error);
140
+ this.setState({
141
+ error: null,
142
+ componentStack: []
143
+ });
144
+ };
145
+ render() {
146
+ const { error } = this.state;
147
+ if (error !== null) {
148
+ const { fallback } = this.props;
149
+ if (typeof fallback === "function") return fallback({
150
+ error,
151
+ componentStack: this.state.componentStack,
152
+ resetErrorBoundary: this.reset
153
+ });
154
+ return fallback ?? null;
155
+ }
156
+ return this.props.children;
157
+ }
158
+ };
159
+
160
+ //#endregion
161
+ //#region src/flareReactErrorHandler.ts
162
+ function flareReactErrorHandler(options) {
163
+ return (error, errorInfo) => {
164
+ const errorObject = (0, _flareapp_js.convertToError)(error);
165
+ options?.beforeEvaluate?.({
166
+ error: errorObject,
167
+ errorInfo
168
+ });
169
+ const rawStack = errorInfo.componentStack ?? "";
170
+ const context = { react: {
171
+ componentStack: formatComponentStack(rawStack),
172
+ componentStackFrames: parseComponentStack(rawStack)
173
+ } };
174
+ const finalContext = options?.beforeSubmit?.({
175
+ error: errorObject,
176
+ errorInfo,
177
+ context
178
+ }) ?? context;
179
+ _flareapp_js.flare.reportSilently(errorObject, contextToAttributes(finalContext));
180
+ options?.afterSubmit?.({
181
+ error: errorObject,
182
+ errorInfo,
183
+ context: finalContext
184
+ });
185
+ };
186
+ }
187
+
188
+ //#endregion
189
+ //#region src/index.ts
190
+ registerReactSdkIdentity();
191
+
192
+ //#endregion
193
+ exports.FlareErrorBoundary = FlareErrorBoundary;
194
+ exports.flareReactErrorHandler = flareReactErrorHandler;
@@ -0,0 +1,83 @@
1
+ import { Component, ErrorInfo, PropsWithChildren, ReactNode } from "react";
2
+
3
+ //#region src/types.d.ts
4
+ type ComponentStackFrame = {
5
+ component: string;
6
+ file: string | null;
7
+ line: number | null;
8
+ column: number | null;
9
+ };
10
+ type FlareReactContext = {
11
+ react: {
12
+ componentStack: string[];
13
+ componentStackFrames: ComponentStackFrame[];
14
+ };
15
+ };
16
+ //#endregion
17
+ //#region src/FlareErrorBoundary.d.ts
18
+ type FlareErrorBoundaryFallbackProps = {
19
+ error: Error;
20
+ componentStack: string[];
21
+ resetErrorBoundary: () => void;
22
+ };
23
+ type FlareErrorBoundaryProps = PropsWithChildren<{
24
+ fallback?: ReactNode | ((props: FlareErrorBoundaryFallbackProps) => ReactNode);
25
+ resetKeys?: unknown[];
26
+ beforeEvaluate?: (params: {
27
+ error: Error;
28
+ errorInfo: ErrorInfo;
29
+ }) => void;
30
+ beforeSubmit?: (params: {
31
+ error: Error;
32
+ errorInfo: ErrorInfo;
33
+ context: FlareReactContext;
34
+ }) => FlareReactContext;
35
+ afterSubmit?: (params: {
36
+ error: Error;
37
+ errorInfo: ErrorInfo;
38
+ context: FlareReactContext;
39
+ }) => void;
40
+ onReset?: (error: Error | null) => void;
41
+ }>;
42
+ type FlareErrorBoundaryState = {
43
+ error: Error | null;
44
+ componentStack: string[];
45
+ };
46
+ declare class FlareErrorBoundary extends Component<FlareErrorBoundaryProps, FlareErrorBoundaryState> {
47
+ state: FlareErrorBoundaryState;
48
+ static getDerivedStateFromError(error: Error): Partial<FlareErrorBoundaryState>;
49
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
50
+ componentDidUpdate(prevProps: FlareErrorBoundaryProps): void;
51
+ reset: () => void;
52
+ render(): ReactNode;
53
+ }
54
+ //#endregion
55
+ //#region src/flareReactErrorHandler.d.ts
56
+ type FlareReactErrorHandlerCallback = (error: unknown, errorInfo: {
57
+ componentStack?: string;
58
+ }) => void;
59
+ type FlareReactErrorHandlerOptions = {
60
+ beforeEvaluate?: (params: {
61
+ error: Error;
62
+ errorInfo: {
63
+ componentStack?: string;
64
+ };
65
+ }) => void;
66
+ beforeSubmit?: (params: {
67
+ error: Error;
68
+ errorInfo: {
69
+ componentStack?: string;
70
+ };
71
+ context: FlareReactContext;
72
+ }) => FlareReactContext;
73
+ afterSubmit?: (params: {
74
+ error: Error;
75
+ errorInfo: {
76
+ componentStack?: string;
77
+ };
78
+ context: FlareReactContext;
79
+ }) => void;
80
+ };
81
+ declare function flareReactErrorHandler(options?: FlareReactErrorHandlerOptions): FlareReactErrorHandlerCallback;
82
+ //#endregion
83
+ export { type ComponentStackFrame, FlareErrorBoundary, type FlareErrorBoundaryFallbackProps, type FlareErrorBoundaryProps, type FlareReactContext, type FlareReactErrorHandlerCallback, type FlareReactErrorHandlerOptions, flareReactErrorHandler };
package/dist/index.d.mts CHANGED
@@ -1,9 +1,83 @@
1
- import * as react from 'react';
2
- import { Component, PropsWithChildren } from 'react';
1
+ import { Component, ErrorInfo, PropsWithChildren, ReactNode } from "react";
3
2
 
4
- declare class FlareErrorBoundary extends Component<PropsWithChildren> {
5
- componentDidCatch(error: Error, reactErrorInfo: React.ErrorInfo): void;
6
- render(): react.ReactNode;
3
+ //#region src/types.d.ts
4
+ type ComponentStackFrame = {
5
+ component: string;
6
+ file: string | null;
7
+ line: number | null;
8
+ column: number | null;
9
+ };
10
+ type FlareReactContext = {
11
+ react: {
12
+ componentStack: string[];
13
+ componentStackFrames: ComponentStackFrame[];
14
+ };
15
+ };
16
+ //#endregion
17
+ //#region src/FlareErrorBoundary.d.ts
18
+ type FlareErrorBoundaryFallbackProps = {
19
+ error: Error;
20
+ componentStack: string[];
21
+ resetErrorBoundary: () => void;
22
+ };
23
+ type FlareErrorBoundaryProps = PropsWithChildren<{
24
+ fallback?: ReactNode | ((props: FlareErrorBoundaryFallbackProps) => ReactNode);
25
+ resetKeys?: unknown[];
26
+ beforeEvaluate?: (params: {
27
+ error: Error;
28
+ errorInfo: ErrorInfo;
29
+ }) => void;
30
+ beforeSubmit?: (params: {
31
+ error: Error;
32
+ errorInfo: ErrorInfo;
33
+ context: FlareReactContext;
34
+ }) => FlareReactContext;
35
+ afterSubmit?: (params: {
36
+ error: Error;
37
+ errorInfo: ErrorInfo;
38
+ context: FlareReactContext;
39
+ }) => void;
40
+ onReset?: (error: Error | null) => void;
41
+ }>;
42
+ type FlareErrorBoundaryState = {
43
+ error: Error | null;
44
+ componentStack: string[];
45
+ };
46
+ declare class FlareErrorBoundary extends Component<FlareErrorBoundaryProps, FlareErrorBoundaryState> {
47
+ state: FlareErrorBoundaryState;
48
+ static getDerivedStateFromError(error: Error): Partial<FlareErrorBoundaryState>;
49
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
50
+ componentDidUpdate(prevProps: FlareErrorBoundaryProps): void;
51
+ reset: () => void;
52
+ render(): ReactNode;
7
53
  }
8
-
9
- export { FlareErrorBoundary };
54
+ //#endregion
55
+ //#region src/flareReactErrorHandler.d.ts
56
+ type FlareReactErrorHandlerCallback = (error: unknown, errorInfo: {
57
+ componentStack?: string;
58
+ }) => void;
59
+ type FlareReactErrorHandlerOptions = {
60
+ beforeEvaluate?: (params: {
61
+ error: Error;
62
+ errorInfo: {
63
+ componentStack?: string;
64
+ };
65
+ }) => void;
66
+ beforeSubmit?: (params: {
67
+ error: Error;
68
+ errorInfo: {
69
+ componentStack?: string;
70
+ };
71
+ context: FlareReactContext;
72
+ }) => FlareReactContext;
73
+ afterSubmit?: (params: {
74
+ error: Error;
75
+ errorInfo: {
76
+ componentStack?: string;
77
+ };
78
+ context: FlareReactContext;
79
+ }) => void;
80
+ };
81
+ declare function flareReactErrorHandler(options?: FlareReactErrorHandlerOptions): FlareReactErrorHandlerCallback;
82
+ //#endregion
83
+ export { type ComponentStackFrame, FlareErrorBoundary, type FlareErrorBoundaryFallbackProps, type FlareErrorBoundaryProps, type FlareReactContext, type FlareReactErrorHandlerCallback, type FlareReactErrorHandlerOptions, flareReactErrorHandler };
package/dist/index.mjs CHANGED
@@ -1,22 +1,165 @@
1
- // src/index.ts
2
- import { flare } from "@flareapp/js";
1
+ import { convertToError, flare } from "@flareapp/js";
2
+ import * as React from "react";
3
3
  import { Component } from "react";
4
+
5
+ //#region src/constants.ts
6
+ const CHROMIUM_STACK_REGEX = /^at\s+(\S+)(?:\s+\((.+):(\d+):(\d+)\))?$/;
7
+ const FIREFOX_SAFARI_STACK_REGEX = /^(\S+?)@(.+):(\d+):(\d+)$/;
8
+ const PACKAGE_VERSION = typeof process !== "undefined" && true ? "2.0.1" : "?";
9
+
10
+ //#endregion
11
+ //#region src/identify.ts
12
+ let registered = false;
13
+ function registerReactSdkIdentity() {
14
+ if (registered) return;
15
+ registered = true;
16
+ flare.setSdkInfo({
17
+ name: "@flareapp/react",
18
+ version: PACKAGE_VERSION
19
+ });
20
+ flare.setFramework({
21
+ name: "React",
22
+ version: React.version
23
+ });
24
+ }
25
+
26
+ //#endregion
27
+ //#region src/contextToAttributes.ts
28
+ function contextToAttributes(context) {
29
+ return { "context.custom": { react: {
30
+ componentStack: context.react.componentStack,
31
+ componentStackFrames: context.react.componentStackFrames
32
+ } } };
33
+ }
34
+
35
+ //#endregion
36
+ //#region src/formatComponentStack.ts
37
+ function formatComponentStack(stack) {
38
+ return stack.split(/\s*\n\s*/g).filter((line) => line.length > 0);
39
+ }
40
+
41
+ //#endregion
42
+ //#region src/parseComponentStack.ts
43
+ function parseComponentStack(stack) {
44
+ return stack.split(/\s*\n\s*/g).filter((line) => line.length > 0).map((line) => {
45
+ const chromeMatch = line.match(CHROMIUM_STACK_REGEX);
46
+ if (chromeMatch) return {
47
+ component: chromeMatch[1],
48
+ file: chromeMatch[2] ?? null,
49
+ line: chromeMatch[3] ? Number(chromeMatch[3]) : null,
50
+ column: chromeMatch[4] ? Number(chromeMatch[4]) : null
51
+ };
52
+ const firefoxSafariMatch = line.match(FIREFOX_SAFARI_STACK_REGEX);
53
+ if (firefoxSafariMatch) return {
54
+ component: firefoxSafariMatch[1],
55
+ file: firefoxSafariMatch[2],
56
+ line: Number(firefoxSafariMatch[3]),
57
+ column: Number(firefoxSafariMatch[4])
58
+ };
59
+ return {
60
+ component: line.replace(/^at\s+/, ""),
61
+ file: null,
62
+ line: null,
63
+ column: null
64
+ };
65
+ });
66
+ }
67
+
68
+ //#endregion
69
+ //#region src/FlareErrorBoundary.ts
4
70
  var FlareErrorBoundary = class extends Component {
5
- componentDidCatch(error, reactErrorInfo) {
6
- const context = {
7
- react: {
8
- componentStack: formatReactComponentStack(reactErrorInfo?.componentStack ?? "")
9
- }
10
- };
11
- flare.report(error, context, { react: { errorInfo: reactErrorInfo } });
12
- }
13
- render() {
14
- return this.props.children;
15
- }
71
+ state = {
72
+ error: null,
73
+ componentStack: []
74
+ };
75
+ static getDerivedStateFromError(error) {
76
+ return { error };
77
+ }
78
+ componentDidCatch(error, errorInfo) {
79
+ this.props.beforeEvaluate?.({
80
+ error,
81
+ errorInfo
82
+ });
83
+ const rawStack = errorInfo.componentStack ?? "";
84
+ const context = { react: {
85
+ componentStack: formatComponentStack(rawStack),
86
+ componentStackFrames: parseComponentStack(rawStack)
87
+ } };
88
+ const finalContext = this.props.beforeSubmit?.({
89
+ error,
90
+ errorInfo,
91
+ context
92
+ }) ?? context;
93
+ this.setState({ componentStack: finalContext.react.componentStack });
94
+ flare.reportSilently(error, contextToAttributes(finalContext));
95
+ this.props.afterSubmit?.({
96
+ error,
97
+ errorInfo,
98
+ context: finalContext
99
+ });
100
+ }
101
+ componentDidUpdate(prevProps) {
102
+ if (this.state.error === null || !this.props.resetKeys) return;
103
+ const prevKeys = prevProps.resetKeys;
104
+ const nextKeys = this.props.resetKeys;
105
+ const lengthChanged = prevKeys?.length !== nextKeys.length;
106
+ const valuesChanged = nextKeys.some((key, i) => !Object.is(key, prevKeys?.[i]));
107
+ if (lengthChanged || valuesChanged) this.reset();
108
+ }
109
+ reset = () => {
110
+ const { error } = this.state;
111
+ this.props.onReset?.(error);
112
+ this.setState({
113
+ error: null,
114
+ componentStack: []
115
+ });
116
+ };
117
+ render() {
118
+ const { error } = this.state;
119
+ if (error !== null) {
120
+ const { fallback } = this.props;
121
+ if (typeof fallback === "function") return fallback({
122
+ error,
123
+ componentStack: this.state.componentStack,
124
+ resetErrorBoundary: this.reset
125
+ });
126
+ return fallback ?? null;
127
+ }
128
+ return this.props.children;
129
+ }
16
130
  };
17
- function formatReactComponentStack(stack) {
18
- return stack.split(/\s*\n\s*/g).filter((line) => line.length > 0);
131
+
132
+ //#endregion
133
+ //#region src/flareReactErrorHandler.ts
134
+ function flareReactErrorHandler(options) {
135
+ return (error, errorInfo) => {
136
+ const errorObject = convertToError(error);
137
+ options?.beforeEvaluate?.({
138
+ error: errorObject,
139
+ errorInfo
140
+ });
141
+ const rawStack = errorInfo.componentStack ?? "";
142
+ const context = { react: {
143
+ componentStack: formatComponentStack(rawStack),
144
+ componentStackFrames: parseComponentStack(rawStack)
145
+ } };
146
+ const finalContext = options?.beforeSubmit?.({
147
+ error: errorObject,
148
+ errorInfo,
149
+ context
150
+ }) ?? context;
151
+ flare.reportSilently(errorObject, contextToAttributes(finalContext));
152
+ options?.afterSubmit?.({
153
+ error: errorObject,
154
+ errorInfo,
155
+ context: finalContext
156
+ });
157
+ };
19
158
  }
20
- export {
21
- FlareErrorBoundary
22
- };
159
+
160
+ //#endregion
161
+ //#region src/index.ts
162
+ registerReactSdkIdentity();
163
+
164
+ //#endregion
165
+ export { FlareErrorBoundary, flareReactErrorHandler };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flareapp/react",
3
- "version": "1.2.0",
3
+ "version": "2.0.1",
4
4
  "description": "React client for flareapp.io",
5
5
  "homepage": "https://flareapp.io",
6
6
  "bugs": "https://github.com/spatie/flare-client-js/issues",
@@ -9,31 +9,55 @@
9
9
  "url": "git+https://github.com/spatie/flare-client-js.git"
10
10
  },
11
11
  "license": "MIT",
12
- "author": "adriaan@spatie.be",
13
- "main": "./dist/index.js",
12
+ "author": {
13
+ "name": "Spatie",
14
+ "email": "info@spatie.be"
15
+ },
16
+ "contributors": [
17
+ "Adriaan Marain <adriaan@spatie.be>",
18
+ "Alex Vanderbist <alex@spatie.be>",
19
+ "Dries Heyninck <dries@spatie.be>",
20
+ "Freek Van der Herten <freek@spatie.be>",
21
+ "Sebastian De Deyne <sebastian@spatie.be>",
22
+ "Sébastien Henau <seba@spatie.be>"
23
+ ],
24
+ "main": "./dist/index.cjs",
14
25
  "module": "./dist/index.mjs",
15
- "types": "./dist/index.d.ts",
26
+ "types": "./dist/index.d.cts",
16
27
  "exports": {
17
28
  ".": {
18
- "require": "./dist/index.js",
19
- "import": "./dist/index.mjs"
29
+ "import": {
30
+ "types": "./dist/index.d.mts",
31
+ "default": "./dist/index.mjs"
32
+ },
33
+ "require": {
34
+ "types": "./dist/index.d.cts",
35
+ "default": "./dist/index.cjs"
36
+ }
20
37
  }
21
38
  },
22
39
  "scripts": {
23
40
  "prepublishOnly": "npm run build",
24
- "build": "tsup src/index.ts --format cjs,esm --dts --clean",
41
+ "build": "tsdown src/index.ts --format cjs,esm --dts --env.PACKAGE_VERSION=$(node -p \"require('./package.json').version\") --clean",
42
+ "test": "vitest run",
25
43
  "typescript": "tsc --noEmit",
26
- "release": "npx release-it"
44
+ "release": "release-it"
27
45
  },
28
46
  "devDependencies": {
29
47
  "@flareapp/js": "file:../js",
30
- "@types/react": "^18.2.47",
48
+ "@testing-library/jest-dom": "^6.9.1",
49
+ "@testing-library/react": "^16.0.0",
50
+ "@types/react": "^19.0.0",
51
+ "@types/react-dom": "^19.0.0",
52
+ "jsdom": "^26.1.0",
31
53
  "react": "^19.0.0",
32
- "tsup": "^8.0.1",
33
- "typescript": "^5.3.3"
54
+ "react-dom": "^19.0.0",
55
+ "tsdown": "^0.20.3",
56
+ "typescript": "^5.7.0",
57
+ "vitest": "^4.0.0"
34
58
  },
35
59
  "peerDependencies": {
36
- "@flareapp/js": "^1.2.0",
60
+ "@flareapp/js": "^2.0.1",
37
61
  "react": "^16.0.0||^17.0.0||^18.0.0||^19.0.0"
38
62
  },
39
63
  "publishConfig": {
package/.release-it.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "git": {
3
- "commitMessage": "Release @flareapp/react v${version}",
4
- "tagName": "@flareapp/react@${version}",
5
- "tagAnnotation": "Release @flareapp/react v${version}"
6
- },
7
- "github": {
8
- "release": false
9
- },
10
- "npm": {
11
- "publish": true
12
- },
13
- "hooks": {
14
- "after:bump": "npm run build"
15
- }
16
- }
package/dist/index.d.ts DELETED
@@ -1,9 +0,0 @@
1
- import * as react from 'react';
2
- import { Component, PropsWithChildren } from 'react';
3
-
4
- declare class FlareErrorBoundary extends Component<PropsWithChildren> {
5
- componentDidCatch(error: Error, reactErrorInfo: React.ErrorInfo): void;
6
- render(): react.ReactNode;
7
- }
8
-
9
- export { FlareErrorBoundary };
package/dist/index.js DELETED
@@ -1,47 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- FlareErrorBoundary: () => FlareErrorBoundary
24
- });
25
- module.exports = __toCommonJS(index_exports);
26
- var import_js = require("@flareapp/js");
27
- var import_react = require("react");
28
- var FlareErrorBoundary = class extends import_react.Component {
29
- componentDidCatch(error, reactErrorInfo) {
30
- const context = {
31
- react: {
32
- componentStack: formatReactComponentStack(reactErrorInfo?.componentStack ?? "")
33
- }
34
- };
35
- import_js.flare.report(error, context, { react: { errorInfo: reactErrorInfo } });
36
- }
37
- render() {
38
- return this.props.children;
39
- }
40
- };
41
- function formatReactComponentStack(stack) {
42
- return stack.split(/\s*\n\s*/g).filter((line) => line.length > 0);
43
- }
44
- // Annotate the CommonJS export names for ESM import in node:
45
- 0 && (module.exports = {
46
- FlareErrorBoundary
47
- });
package/src/index.ts DELETED
@@ -1,28 +0,0 @@
1
- import { flare } from '@flareapp/js';
2
- import { Component, PropsWithChildren } from 'react';
3
-
4
- interface Context {
5
- react: {
6
- componentStack: Array<String>;
7
- };
8
- }
9
-
10
- export class FlareErrorBoundary extends Component<PropsWithChildren> {
11
- componentDidCatch(error: Error, reactErrorInfo: React.ErrorInfo) {
12
- const context: Context = {
13
- react: {
14
- componentStack: formatReactComponentStack(reactErrorInfo?.componentStack ?? ''),
15
- },
16
- };
17
-
18
- flare.report(error, context, { react: { errorInfo: reactErrorInfo } });
19
- }
20
-
21
- render() {
22
- return this.props.children;
23
- }
24
- }
25
-
26
- function formatReactComponentStack(stack: string) {
27
- return stack.split(/\s*\n\s*/g).filter((line) => line.length > 0);
28
- }
package/tsconfig.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "include": ["src"]
4
- }