@flareapp/react 2.2.1 → 2.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # @flareapp/react
2
2
 
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.
3
+ React integration for [Flare](https://flareapp.io) error tracking and logging. Provides an error boundary component and
4
+ a React 19+ error handler for catching and reporting React component errors to Flare.
5
5
 
6
6
  ## Installation
7
7
 
@@ -28,6 +28,39 @@ function App() {
28
28
  }
29
29
  ```
30
30
 
31
+ ## Logging
32
+
33
+ Beyond errors, the client can send structured logs. Logs are opt-in: enable them with `enableLogs`, then call any of the
34
+ eight syslog levels (`debug`, `info`, `notice`, `warning`, `error`, `critical`, `alert`, `emergency`).
35
+
36
+ ```tsx
37
+ import { flare } from '@flareapp/js';
38
+
39
+ flare.configure({ enableLogs: true });
40
+
41
+ flare.logger.info('Checkout started', { cartId: cart.id, total: cart.total });
42
+ ```
43
+
44
+ ## Minified production errors
45
+
46
+ In production, React throws minified errors like `Minified React error #418; visit https://react.dev/errors/418?args[]=Foo`.
47
+ The client parses these into structured fields and attaches them, along with the running React version, to the report
48
+ context:
49
+
50
+ ```ts
51
+ react: {
52
+ version: '19.0.0',
53
+ minifiedError: {
54
+ number: 418,
55
+ args: ['Foo', 'Bar'],
56
+ url: 'https://react.dev/errors/418?args[]=Foo&args[]=Bar',
57
+ },
58
+ }
59
+ ```
60
+
61
+ Flare uses `react.minifiedError` and `react.version` on the backend to look up React's error-code map and surface the
62
+ full, human-readable message. No error-code map is bundled into the client. Non-minified errors are reported unchanged.
63
+
31
64
  ## Documentation
32
65
 
33
66
  Full documentation on the error boundary, the React 19+ error handler, lifecycle callbacks, and more is available
package/dist/index.cjs CHANGED
@@ -33,7 +33,7 @@ react = __toESM(react);
33
33
  //#region src/constants.ts
34
34
  const CHROMIUM_STACK_REGEX = /^at\s+(\S+)(?:\s+\((.+):(\d+):(\d+)\))?$/;
35
35
  const FIREFOX_SAFARI_STACK_REGEX = /^(\S+?)@(.+):(\d+):(\d+)$/;
36
- const PACKAGE_VERSION = typeof process !== "undefined" && true ? "2.2.1" : "?";
36
+ const PACKAGE_VERSION = typeof process !== "undefined" && true ? "2.4.0" : "?";
37
37
 
38
38
  //#endregion
39
39
  //#region src/identify.ts
@@ -51,15 +51,6 @@ function registerReactSdkIdentity() {
51
51
  });
52
52
  }
53
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
54
  //#endregion
64
55
  //#region src/formatComponentStack.ts
65
56
  function formatComponentStack(stack) {
@@ -93,6 +84,56 @@ function parseComponentStack(stack) {
93
84
  });
94
85
  }
95
86
 
87
+ //#endregion
88
+ //#region src/parseMinifiedReactError.ts
89
+ const NUMBER_PATTERN = /Minified React error #(\d+)/;
90
+ const ARG_PATTERN = /args\[\]=([^&\s]*)/g;
91
+ const URL_PATTERN = /(https?:\/\/\S+)/;
92
+ function safeDecode(value) {
93
+ try {
94
+ return decodeURIComponent(value);
95
+ } catch {
96
+ return value;
97
+ }
98
+ }
99
+ function parseMinifiedReactError(error) {
100
+ const message = error?.message;
101
+ if (!message) return null;
102
+ const numberMatch = message.match(NUMBER_PATTERN);
103
+ if (!numberMatch) return null;
104
+ const args = [];
105
+ for (const match of message.matchAll(ARG_PATTERN)) args.push(safeDecode(match[1]));
106
+ const urlMatch = message.match(URL_PATTERN);
107
+ return {
108
+ number: Number(numberMatch[1]),
109
+ args,
110
+ url: urlMatch ? urlMatch[1] : null
111
+ };
112
+ }
113
+
114
+ //#endregion
115
+ //#region src/buildReactContext.ts
116
+ function buildReactContext(rawStack, error) {
117
+ const minifiedError = parseMinifiedReactError(error);
118
+ return { react: {
119
+ componentStack: formatComponentStack(rawStack),
120
+ componentStackFrames: parseComponentStack(rawStack),
121
+ version: react.version,
122
+ ...minifiedError ? { minifiedError } : {}
123
+ } };
124
+ }
125
+
126
+ //#endregion
127
+ //#region src/contextToAttributes.ts
128
+ function contextToAttributes(context) {
129
+ return { "context.custom": { react: {
130
+ componentStack: context.react.componentStack,
131
+ componentStackFrames: context.react.componentStackFrames,
132
+ ...context.react.version ? { version: context.react.version } : {},
133
+ ...context.react.minifiedError ? { minifiedError: context.react.minifiedError } : {}
134
+ } } };
135
+ }
136
+
96
137
  //#endregion
97
138
  //#region src/FlareErrorBoundary.ts
98
139
  var FlareErrorBoundary = class extends react.Component {
@@ -108,11 +149,7 @@ var FlareErrorBoundary = class extends react.Component {
108
149
  error,
109
150
  errorInfo
110
151
  });
111
- const rawStack = errorInfo.componentStack ?? "";
112
- const context = { react: {
113
- componentStack: formatComponentStack(rawStack),
114
- componentStackFrames: parseComponentStack(rawStack)
115
- } };
152
+ const context = buildReactContext(errorInfo.componentStack ?? "", error);
116
153
  const finalContext = this.props.beforeSubmit?.({
117
154
  error,
118
155
  errorInfo,
@@ -166,11 +203,7 @@ function flareReactErrorHandler(options) {
166
203
  error: errorObject,
167
204
  errorInfo
168
205
  });
169
- const rawStack = errorInfo.componentStack ?? "";
170
- const context = { react: {
171
- componentStack: formatComponentStack(rawStack),
172
- componentStackFrames: parseComponentStack(rawStack)
173
- } };
206
+ const context = buildReactContext(errorInfo.componentStack ?? "", errorObject);
174
207
  const finalContext = options?.beforeSubmit?.({
175
208
  error: errorObject,
176
209
  errorInfo,
package/dist/index.d.cts CHANGED
@@ -7,10 +7,17 @@ type ComponentStackFrame = {
7
7
  line: number | null;
8
8
  column: number | null;
9
9
  };
10
+ type MinifiedReactError = {
11
+ number: number;
12
+ args: string[];
13
+ url: string | null;
14
+ };
10
15
  type FlareReactContext = {
11
16
  react: {
12
17
  componentStack: string[];
13
18
  componentStackFrames: ComponentStackFrame[];
19
+ version?: string;
20
+ minifiedError?: MinifiedReactError;
14
21
  };
15
22
  };
16
23
  //#endregion
@@ -80,4 +87,4 @@ type FlareReactErrorHandlerOptions = {
80
87
  };
81
88
  declare function flareReactErrorHandler(options?: FlareReactErrorHandlerOptions): FlareReactErrorHandlerCallback;
82
89
  //#endregion
83
- export { type ComponentStackFrame, FlareErrorBoundary, type FlareErrorBoundaryFallbackProps, type FlareErrorBoundaryProps, type FlareReactContext, type FlareReactErrorHandlerCallback, type FlareReactErrorHandlerOptions, flareReactErrorHandler };
90
+ export { type ComponentStackFrame, FlareErrorBoundary, type FlareErrorBoundaryFallbackProps, type FlareErrorBoundaryProps, type FlareReactContext, type FlareReactErrorHandlerCallback, type FlareReactErrorHandlerOptions, type MinifiedReactError, flareReactErrorHandler };
package/dist/index.d.mts CHANGED
@@ -7,10 +7,17 @@ type ComponentStackFrame = {
7
7
  line: number | null;
8
8
  column: number | null;
9
9
  };
10
+ type MinifiedReactError = {
11
+ number: number;
12
+ args: string[];
13
+ url: string | null;
14
+ };
10
15
  type FlareReactContext = {
11
16
  react: {
12
17
  componentStack: string[];
13
18
  componentStackFrames: ComponentStackFrame[];
19
+ version?: string;
20
+ minifiedError?: MinifiedReactError;
14
21
  };
15
22
  };
16
23
  //#endregion
@@ -80,4 +87,4 @@ type FlareReactErrorHandlerOptions = {
80
87
  };
81
88
  declare function flareReactErrorHandler(options?: FlareReactErrorHandlerOptions): FlareReactErrorHandlerCallback;
82
89
  //#endregion
83
- export { type ComponentStackFrame, FlareErrorBoundary, type FlareErrorBoundaryFallbackProps, type FlareErrorBoundaryProps, type FlareReactContext, type FlareReactErrorHandlerCallback, type FlareReactErrorHandlerOptions, flareReactErrorHandler };
90
+ export { type ComponentStackFrame, FlareErrorBoundary, type FlareErrorBoundaryFallbackProps, type FlareErrorBoundaryProps, type FlareReactContext, type FlareReactErrorHandlerCallback, type FlareReactErrorHandlerOptions, type MinifiedReactError, flareReactErrorHandler };
package/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  import { convertToError, flare } from "@flareapp/js";
2
2
  import * as React from "react";
3
- import { Component } from "react";
3
+ import { Component, version } from "react";
4
4
 
5
5
  //#region src/constants.ts
6
6
  const CHROMIUM_STACK_REGEX = /^at\s+(\S+)(?:\s+\((.+):(\d+):(\d+)\))?$/;
7
7
  const FIREFOX_SAFARI_STACK_REGEX = /^(\S+?)@(.+):(\d+):(\d+)$/;
8
- const PACKAGE_VERSION = typeof process !== "undefined" && true ? "2.2.1" : "?";
8
+ const PACKAGE_VERSION = typeof process !== "undefined" && true ? "2.4.0" : "?";
9
9
 
10
10
  //#endregion
11
11
  //#region src/identify.ts
@@ -23,15 +23,6 @@ function registerReactSdkIdentity() {
23
23
  });
24
24
  }
25
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
26
  //#endregion
36
27
  //#region src/formatComponentStack.ts
37
28
  function formatComponentStack(stack) {
@@ -65,6 +56,56 @@ function parseComponentStack(stack) {
65
56
  });
66
57
  }
67
58
 
59
+ //#endregion
60
+ //#region src/parseMinifiedReactError.ts
61
+ const NUMBER_PATTERN = /Minified React error #(\d+)/;
62
+ const ARG_PATTERN = /args\[\]=([^&\s]*)/g;
63
+ const URL_PATTERN = /(https?:\/\/\S+)/;
64
+ function safeDecode(value) {
65
+ try {
66
+ return decodeURIComponent(value);
67
+ } catch {
68
+ return value;
69
+ }
70
+ }
71
+ function parseMinifiedReactError(error) {
72
+ const message = error?.message;
73
+ if (!message) return null;
74
+ const numberMatch = message.match(NUMBER_PATTERN);
75
+ if (!numberMatch) return null;
76
+ const args = [];
77
+ for (const match of message.matchAll(ARG_PATTERN)) args.push(safeDecode(match[1]));
78
+ const urlMatch = message.match(URL_PATTERN);
79
+ return {
80
+ number: Number(numberMatch[1]),
81
+ args,
82
+ url: urlMatch ? urlMatch[1] : null
83
+ };
84
+ }
85
+
86
+ //#endregion
87
+ //#region src/buildReactContext.ts
88
+ function buildReactContext(rawStack, error) {
89
+ const minifiedError = parseMinifiedReactError(error);
90
+ return { react: {
91
+ componentStack: formatComponentStack(rawStack),
92
+ componentStackFrames: parseComponentStack(rawStack),
93
+ version,
94
+ ...minifiedError ? { minifiedError } : {}
95
+ } };
96
+ }
97
+
98
+ //#endregion
99
+ //#region src/contextToAttributes.ts
100
+ function contextToAttributes(context) {
101
+ return { "context.custom": { react: {
102
+ componentStack: context.react.componentStack,
103
+ componentStackFrames: context.react.componentStackFrames,
104
+ ...context.react.version ? { version: context.react.version } : {},
105
+ ...context.react.minifiedError ? { minifiedError: context.react.minifiedError } : {}
106
+ } } };
107
+ }
108
+
68
109
  //#endregion
69
110
  //#region src/FlareErrorBoundary.ts
70
111
  var FlareErrorBoundary = class extends Component {
@@ -80,11 +121,7 @@ var FlareErrorBoundary = class extends Component {
80
121
  error,
81
122
  errorInfo
82
123
  });
83
- const rawStack = errorInfo.componentStack ?? "";
84
- const context = { react: {
85
- componentStack: formatComponentStack(rawStack),
86
- componentStackFrames: parseComponentStack(rawStack)
87
- } };
124
+ const context = buildReactContext(errorInfo.componentStack ?? "", error);
88
125
  const finalContext = this.props.beforeSubmit?.({
89
126
  error,
90
127
  errorInfo,
@@ -138,11 +175,7 @@ function flareReactErrorHandler(options) {
138
175
  error: errorObject,
139
176
  errorInfo
140
177
  });
141
- const rawStack = errorInfo.componentStack ?? "";
142
- const context = { react: {
143
- componentStack: formatComponentStack(rawStack),
144
- componentStackFrames: parseComponentStack(rawStack)
145
- } };
178
+ const context = buildReactContext(errorInfo.componentStack ?? "", errorObject);
146
179
  const finalContext = options?.beforeSubmit?.({
147
180
  error: errorObject,
148
181
  errorInfo,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flareapp/react",
3
- "version": "2.2.1",
3
+ "version": "2.4.0",
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",
@@ -60,7 +60,7 @@
60
60
  "vitest": "^4.0.0"
61
61
  },
62
62
  "peerDependencies": {
63
- "@flareapp/js": "^2.2.1",
63
+ "@flareapp/js": "^2.4.0",
64
64
  "react": "^16.0.0||^17.0.0||^18.0.0||^19.0.0"
65
65
  },
66
66
  "publishConfig": {