@drakkar.software/sunglasses-react 0.10.0 → 0.12.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/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { ISunglassesClient, ScreenTrackingOptions, CaptureExceptionOptions, ConsentStatus } from '@drakkar.software/sunglasses-core';
3
- export { CaptureExceptionOptions, ConsentStatus, ISunglassesClient, ScreenTrackingOptions, SunglassesConfig, SunglassesCore, SunglassesEvent, captureException } from '@drakkar.software/sunglasses-core';
2
+ import { ISunglassesClient, ScreenTrackingOptions, AutoCaptureErrorsOptions, ConsentStatus, CaptureExceptionOptions } from '@drakkar.software/sunglasses-core';
3
+ export { AutoCaptureErrorsOptions, CaptureExceptionOptions, ConsentStatus, ConsoleCaptureOptions, GlobalErrorInfo, GlobalErrorListener, ISunglassesClient, ScreenTrackingOptions, SunglassesConfig, SunglassesCore, SunglassesEvent, captureException, patchConsole, publishGlobalError, subscribeGlobalError } from '@drakkar.software/sunglasses-core';
4
4
 
5
5
  interface SunglassesProviderProps {
6
6
  /** An initialized ISunglassesClient (from SunglassesCore.create()). */
@@ -8,12 +8,19 @@ interface SunglassesProviderProps {
8
8
  /** Optional screen tracking configuration. */
9
9
  screenTracking?: ScreenTrackingOptions;
10
10
  /**
11
- * Automatically capture unhandled global errors as `$error` events
12
- * (`$error_handled: false`). Listens to `window` `'error'` and
13
- * `'unhandledrejection'`. Pass `true` for defaults, or an options object to
14
- * configure truncation / stack inclusion. Default: off.
11
+ * Automatically capture unhandled errors as `$error` events
12
+ * (`$error_handled: false`).
13
+ *
14
+ * - `true` installs the global handlers for `window` `'error'` and
15
+ * `'unhandledrejection'`.
16
+ * - An options object additionally lets you toggle `globalHandlers` and
17
+ * `unhandledRejections`, opt into `console` capture (`console.error` /
18
+ * `console.warn`), plus configure truncation / stack inclusion / ignore
19
+ * patterns.
20
+ *
21
+ * Default: off.
15
22
  */
16
- autoCaptureErrors?: boolean | CaptureExceptionOptions;
23
+ autoCaptureErrors?: boolean | AutoCaptureErrorsOptions;
17
24
  children: React.ReactNode;
18
25
  }
19
26
  /**
@@ -156,4 +163,49 @@ interface SunglassesErrorBoundaryProps {
156
163
  */
157
164
  declare function SunglassesErrorBoundary(props: SunglassesErrorBoundaryProps): React.ReactElement;
158
165
 
159
- export { SunglassesErrorBoundary, type SunglassesErrorBoundaryProps, SunglassesProvider, type SunglassesProviderProps, captureUtmParams, useCapture, useConsentStatus, useScreenTracking, useSunglasses };
166
+ interface SunglassesGlobalErrorBoundaryProps {
167
+ /**
168
+ * SunGlasses client. Optional — defaults to the client provided by the
169
+ * nearest `<SunglassesProvider>`.
170
+ */
171
+ client?: ISunglassesClient;
172
+ /** Rendered when an error is caught. Defaults to rendering nothing. */
173
+ fallback?: React.ReactNode;
174
+ /** Error capture configuration forwarded to `captureException`. */
175
+ config?: CaptureExceptionOptions;
176
+ /**
177
+ * Also render the fallback for non-fatal global errors (e.g. `ErrorUtils`
178
+ * errors reported as non-fatal). Default: `false`.
179
+ */
180
+ includeNonFatalGlobalErrors?: boolean;
181
+ /**
182
+ * Also render the fallback for unhandled promise rejections. Off by default
183
+ * because many apps prefer to surface rejections as toasts or inline errors
184
+ * rather than as a full-screen fallback. Default: `false`.
185
+ */
186
+ includeUnhandledRejections?: boolean;
187
+ children: React.ReactNode;
188
+ }
189
+ /**
190
+ * A superset of `SunglassesErrorBoundary` that renders a fallback UI for fatal
191
+ * non-render errors (uncaught errors and, optionally, unhandled rejections) in
192
+ * addition to the render-phase errors a normal error boundary catches.
193
+ *
194
+ * Render-phase errors are captured here as `$error` events
195
+ * (`$error_handled: true`). Global errors are captured by the provider's
196
+ * `autoCaptureErrors` handlers and merely surfaced here as a fallback — so the
197
+ * global fallback requires `autoCaptureErrors` to be enabled on the
198
+ * `<SunglassesProvider>`. No event is captured twice.
199
+ *
200
+ * @example
201
+ * ```tsx
202
+ * <SunglassesProvider client={client} autoCaptureErrors>
203
+ * <SunglassesGlobalErrorBoundary fallback={<ErrorScreen />}>
204
+ * <App />
205
+ * </SunglassesGlobalErrorBoundary>
206
+ * </SunglassesProvider>
207
+ * ```
208
+ */
209
+ declare function SunglassesGlobalErrorBoundary(props: SunglassesGlobalErrorBoundaryProps): React.ReactElement;
210
+
211
+ export { SunglassesErrorBoundary, type SunglassesErrorBoundaryProps, SunglassesGlobalErrorBoundary, type SunglassesGlobalErrorBoundaryProps, SunglassesProvider, type SunglassesProviderProps, captureUtmParams, useCapture, useConsentStatus, useScreenTracking, useSunglasses };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { ISunglassesClient, ScreenTrackingOptions, CaptureExceptionOptions, ConsentStatus } from '@drakkar.software/sunglasses-core';
3
- export { CaptureExceptionOptions, ConsentStatus, ISunglassesClient, ScreenTrackingOptions, SunglassesConfig, SunglassesCore, SunglassesEvent, captureException } from '@drakkar.software/sunglasses-core';
2
+ import { ISunglassesClient, ScreenTrackingOptions, AutoCaptureErrorsOptions, ConsentStatus, CaptureExceptionOptions } from '@drakkar.software/sunglasses-core';
3
+ export { AutoCaptureErrorsOptions, CaptureExceptionOptions, ConsentStatus, ConsoleCaptureOptions, GlobalErrorInfo, GlobalErrorListener, ISunglassesClient, ScreenTrackingOptions, SunglassesConfig, SunglassesCore, SunglassesEvent, captureException, patchConsole, publishGlobalError, subscribeGlobalError } from '@drakkar.software/sunglasses-core';
4
4
 
5
5
  interface SunglassesProviderProps {
6
6
  /** An initialized ISunglassesClient (from SunglassesCore.create()). */
@@ -8,12 +8,19 @@ interface SunglassesProviderProps {
8
8
  /** Optional screen tracking configuration. */
9
9
  screenTracking?: ScreenTrackingOptions;
10
10
  /**
11
- * Automatically capture unhandled global errors as `$error` events
12
- * (`$error_handled: false`). Listens to `window` `'error'` and
13
- * `'unhandledrejection'`. Pass `true` for defaults, or an options object to
14
- * configure truncation / stack inclusion. Default: off.
11
+ * Automatically capture unhandled errors as `$error` events
12
+ * (`$error_handled: false`).
13
+ *
14
+ * - `true` installs the global handlers for `window` `'error'` and
15
+ * `'unhandledrejection'`.
16
+ * - An options object additionally lets you toggle `globalHandlers` and
17
+ * `unhandledRejections`, opt into `console` capture (`console.error` /
18
+ * `console.warn`), plus configure truncation / stack inclusion / ignore
19
+ * patterns.
20
+ *
21
+ * Default: off.
15
22
  */
16
- autoCaptureErrors?: boolean | CaptureExceptionOptions;
23
+ autoCaptureErrors?: boolean | AutoCaptureErrorsOptions;
17
24
  children: React.ReactNode;
18
25
  }
19
26
  /**
@@ -156,4 +163,49 @@ interface SunglassesErrorBoundaryProps {
156
163
  */
157
164
  declare function SunglassesErrorBoundary(props: SunglassesErrorBoundaryProps): React.ReactElement;
158
165
 
159
- export { SunglassesErrorBoundary, type SunglassesErrorBoundaryProps, SunglassesProvider, type SunglassesProviderProps, captureUtmParams, useCapture, useConsentStatus, useScreenTracking, useSunglasses };
166
+ interface SunglassesGlobalErrorBoundaryProps {
167
+ /**
168
+ * SunGlasses client. Optional — defaults to the client provided by the
169
+ * nearest `<SunglassesProvider>`.
170
+ */
171
+ client?: ISunglassesClient;
172
+ /** Rendered when an error is caught. Defaults to rendering nothing. */
173
+ fallback?: React.ReactNode;
174
+ /** Error capture configuration forwarded to `captureException`. */
175
+ config?: CaptureExceptionOptions;
176
+ /**
177
+ * Also render the fallback for non-fatal global errors (e.g. `ErrorUtils`
178
+ * errors reported as non-fatal). Default: `false`.
179
+ */
180
+ includeNonFatalGlobalErrors?: boolean;
181
+ /**
182
+ * Also render the fallback for unhandled promise rejections. Off by default
183
+ * because many apps prefer to surface rejections as toasts or inline errors
184
+ * rather than as a full-screen fallback. Default: `false`.
185
+ */
186
+ includeUnhandledRejections?: boolean;
187
+ children: React.ReactNode;
188
+ }
189
+ /**
190
+ * A superset of `SunglassesErrorBoundary` that renders a fallback UI for fatal
191
+ * non-render errors (uncaught errors and, optionally, unhandled rejections) in
192
+ * addition to the render-phase errors a normal error boundary catches.
193
+ *
194
+ * Render-phase errors are captured here as `$error` events
195
+ * (`$error_handled: true`). Global errors are captured by the provider's
196
+ * `autoCaptureErrors` handlers and merely surfaced here as a fallback — so the
197
+ * global fallback requires `autoCaptureErrors` to be enabled on the
198
+ * `<SunglassesProvider>`. No event is captured twice.
199
+ *
200
+ * @example
201
+ * ```tsx
202
+ * <SunglassesProvider client={client} autoCaptureErrors>
203
+ * <SunglassesGlobalErrorBoundary fallback={<ErrorScreen />}>
204
+ * <App />
205
+ * </SunglassesGlobalErrorBoundary>
206
+ * </SunglassesProvider>
207
+ * ```
208
+ */
209
+ declare function SunglassesGlobalErrorBoundary(props: SunglassesGlobalErrorBoundaryProps): React.ReactElement;
210
+
211
+ export { SunglassesErrorBoundary, type SunglassesErrorBoundaryProps, SunglassesGlobalErrorBoundary, type SunglassesGlobalErrorBoundaryProps, SunglassesProvider, type SunglassesProviderProps, captureUtmParams, useCapture, useConsentStatus, useScreenTracking, useSunglasses };
package/dist/index.js CHANGED
@@ -30,11 +30,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- SunglassesCore: () => import_sunglasses_core3.SunglassesCore,
33
+ SunglassesCore: () => import_sunglasses_core4.SunglassesCore,
34
34
  SunglassesErrorBoundary: () => SunglassesErrorBoundary,
35
+ SunglassesGlobalErrorBoundary: () => SunglassesGlobalErrorBoundary,
35
36
  SunglassesProvider: () => SunglassesProvider,
36
- captureException: () => import_sunglasses_core3.captureException,
37
+ captureException: () => import_sunglasses_core4.captureException,
37
38
  captureUtmParams: () => captureUtmParams,
39
+ patchConsole: () => import_sunglasses_core4.patchConsole,
40
+ publishGlobalError: () => import_sunglasses_core4.publishGlobalError,
41
+ subscribeGlobalError: () => import_sunglasses_core4.subscribeGlobalError,
38
42
  useCapture: () => useCapture,
39
43
  useConsentStatus: () => useConsentStatus,
40
44
  useScreenTracking: () => useScreenTracking,
@@ -132,19 +136,33 @@ function SunglassesProvider({
132
136
  }, [client]);
133
137
  (0, import_react3.useEffect)(() => {
134
138
  if (!autoCaptureErrors) return;
135
- if (typeof window === "undefined") return;
136
139
  const options = typeof autoCaptureErrors === "object" ? autoCaptureErrors : {};
137
- const onError = (event) => {
138
- (0, import_sunglasses_core.captureException)(client, event.error ?? event.message, { handled: false, ...options });
139
- };
140
- const onRejection = (event) => {
141
- (0, import_sunglasses_core.captureException)(client, event.reason, { handled: false, ...options });
142
- };
143
- window.addEventListener("error", onError);
144
- window.addEventListener("unhandledrejection", onRejection);
140
+ const cleanups = [];
141
+ if (typeof window !== "undefined") {
142
+ if (options.globalHandlers !== false) {
143
+ const onError = (event) => {
144
+ const error = event.error ?? event.message;
145
+ (0, import_sunglasses_core.captureException)(client, error, { handled: false, ...options });
146
+ (0, import_sunglasses_core.publishGlobalError)({ error, fatal: true, kind: "error" });
147
+ };
148
+ window.addEventListener("error", onError);
149
+ cleanups.push(() => window.removeEventListener("error", onError));
150
+ }
151
+ if (options.unhandledRejections !== false) {
152
+ const onRejection = (event) => {
153
+ (0, import_sunglasses_core.captureException)(client, event.reason, { handled: false, ...options });
154
+ (0, import_sunglasses_core.publishGlobalError)({ error: event.reason, fatal: false, kind: "rejection" });
155
+ };
156
+ window.addEventListener("unhandledrejection", onRejection);
157
+ cleanups.push(() => window.removeEventListener("unhandledrejection", onRejection));
158
+ }
159
+ }
160
+ if (options.console) {
161
+ const consoleOptions = typeof options.console === "object" ? options.console : {};
162
+ cleanups.push((0, import_sunglasses_core.patchConsole)(client, consoleOptions));
163
+ }
145
164
  return () => {
146
- window.removeEventListener("error", onError);
147
- window.removeEventListener("unhandledrejection", onRejection);
165
+ for (const cleanup of cleanups) cleanup();
148
166
  };
149
167
  }, [client, autoCaptureErrors]);
150
168
  (0, import_react3.useEffect)(() => {
@@ -240,15 +258,68 @@ function SunglassesErrorBoundary(props) {
240
258
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ErrorBoundaryInner, { ...props, client });
241
259
  }
242
260
 
243
- // src/index.ts
261
+ // src/SunglassesGlobalErrorBoundary.tsx
262
+ var import_react6 = __toESM(require("react"));
244
263
  var import_sunglasses_core3 = require("@drakkar.software/sunglasses-core");
264
+ var import_jsx_runtime3 = require("react/jsx-runtime");
265
+ var GlobalErrorBoundaryInner = class extends import_react6.default.Component {
266
+ constructor() {
267
+ super(...arguments);
268
+ this.state = { hasError: false };
269
+ }
270
+ static getDerivedStateFromError() {
271
+ return { hasError: true };
272
+ }
273
+ componentDidMount() {
274
+ this.unsubscribe = (0, import_sunglasses_core3.subscribeGlobalError)((info) => this.handleGlobalError(info));
275
+ }
276
+ componentWillUnmount() {
277
+ this.unsubscribe?.();
278
+ }
279
+ componentDidCatch(error) {
280
+ const { client, config } = this.props;
281
+ (0, import_sunglasses_core3.captureException)(client, error, { handled: true, ...config });
282
+ }
283
+ /**
284
+ * React to a global error published by the provider's auto-capture handlers.
285
+ * The provider already captured it, so we only decide whether to show the
286
+ * fallback — we never re-capture here.
287
+ */
288
+ handleGlobalError(info) {
289
+ if (this.state.hasError) return;
290
+ const { includeNonFatalGlobalErrors, includeUnhandledRejections } = this.props;
291
+ const shouldShow = info.kind === "rejection" ? includeUnhandledRejections === true : info.fatal || includeNonFatalGlobalErrors === true;
292
+ if (shouldShow) this.setState({ hasError: true });
293
+ }
294
+ render() {
295
+ if (this.state.hasError) return this.props.fallback ?? null;
296
+ return this.props.children;
297
+ }
298
+ };
299
+ function SunglassesGlobalErrorBoundary(props) {
300
+ const contextClient = (0, import_react6.useContext)(SunglassesContext);
301
+ const client = props.client ?? contextClient;
302
+ if (client === null) {
303
+ throw new Error(
304
+ "[SunGlasses] <SunglassesGlobalErrorBoundary> must be inside a <SunglassesProvider> or receive a `client` prop."
305
+ );
306
+ }
307
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(GlobalErrorBoundaryInner, { ...props, client });
308
+ }
309
+
310
+ // src/index.ts
311
+ var import_sunglasses_core4 = require("@drakkar.software/sunglasses-core");
245
312
  // Annotate the CommonJS export names for ESM import in node:
246
313
  0 && (module.exports = {
247
314
  SunglassesCore,
248
315
  SunglassesErrorBoundary,
316
+ SunglassesGlobalErrorBoundary,
249
317
  SunglassesProvider,
250
318
  captureException,
251
319
  captureUtmParams,
320
+ patchConsole,
321
+ publishGlobalError,
322
+ subscribeGlobalError,
252
323
  useCapture,
253
324
  useConsentStatus,
254
325
  useScreenTracking,
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/SunglassesProvider.tsx
2
2
  import { useEffect as useEffect2 } from "react";
3
- import { captureException } from "@drakkar.software/sunglasses-core";
3
+ import { captureException, patchConsole, publishGlobalError } from "@drakkar.software/sunglasses-core";
4
4
 
5
5
  // src/context.ts
6
6
  import { createContext, useContext } from "react";
@@ -88,19 +88,33 @@ function SunglassesProvider({
88
88
  }, [client]);
89
89
  useEffect2(() => {
90
90
  if (!autoCaptureErrors) return;
91
- if (typeof window === "undefined") return;
92
91
  const options = typeof autoCaptureErrors === "object" ? autoCaptureErrors : {};
93
- const onError = (event) => {
94
- captureException(client, event.error ?? event.message, { handled: false, ...options });
95
- };
96
- const onRejection = (event) => {
97
- captureException(client, event.reason, { handled: false, ...options });
98
- };
99
- window.addEventListener("error", onError);
100
- window.addEventListener("unhandledrejection", onRejection);
92
+ const cleanups = [];
93
+ if (typeof window !== "undefined") {
94
+ if (options.globalHandlers !== false) {
95
+ const onError = (event) => {
96
+ const error = event.error ?? event.message;
97
+ captureException(client, error, { handled: false, ...options });
98
+ publishGlobalError({ error, fatal: true, kind: "error" });
99
+ };
100
+ window.addEventListener("error", onError);
101
+ cleanups.push(() => window.removeEventListener("error", onError));
102
+ }
103
+ if (options.unhandledRejections !== false) {
104
+ const onRejection = (event) => {
105
+ captureException(client, event.reason, { handled: false, ...options });
106
+ publishGlobalError({ error: event.reason, fatal: false, kind: "rejection" });
107
+ };
108
+ window.addEventListener("unhandledrejection", onRejection);
109
+ cleanups.push(() => window.removeEventListener("unhandledrejection", onRejection));
110
+ }
111
+ }
112
+ if (options.console) {
113
+ const consoleOptions = typeof options.console === "object" ? options.console : {};
114
+ cleanups.push(patchConsole(client, consoleOptions));
115
+ }
101
116
  return () => {
102
- window.removeEventListener("error", onError);
103
- window.removeEventListener("unhandledrejection", onRejection);
117
+ for (const cleanup of cleanups) cleanup();
104
118
  };
105
119
  }, [client, autoCaptureErrors]);
106
120
  useEffect2(() => {
@@ -196,14 +210,73 @@ function SunglassesErrorBoundary(props) {
196
210
  return /* @__PURE__ */ jsx2(ErrorBoundaryInner, { ...props, client });
197
211
  }
198
212
 
213
+ // src/SunglassesGlobalErrorBoundary.tsx
214
+ import React3, { useContext as useContext3 } from "react";
215
+ import { captureException as captureException3, subscribeGlobalError } from "@drakkar.software/sunglasses-core";
216
+ import { jsx as jsx3 } from "react/jsx-runtime";
217
+ var GlobalErrorBoundaryInner = class extends React3.Component {
218
+ constructor() {
219
+ super(...arguments);
220
+ this.state = { hasError: false };
221
+ }
222
+ static getDerivedStateFromError() {
223
+ return { hasError: true };
224
+ }
225
+ componentDidMount() {
226
+ this.unsubscribe = subscribeGlobalError((info) => this.handleGlobalError(info));
227
+ }
228
+ componentWillUnmount() {
229
+ this.unsubscribe?.();
230
+ }
231
+ componentDidCatch(error) {
232
+ const { client, config } = this.props;
233
+ captureException3(client, error, { handled: true, ...config });
234
+ }
235
+ /**
236
+ * React to a global error published by the provider's auto-capture handlers.
237
+ * The provider already captured it, so we only decide whether to show the
238
+ * fallback — we never re-capture here.
239
+ */
240
+ handleGlobalError(info) {
241
+ if (this.state.hasError) return;
242
+ const { includeNonFatalGlobalErrors, includeUnhandledRejections } = this.props;
243
+ const shouldShow = info.kind === "rejection" ? includeUnhandledRejections === true : info.fatal || includeNonFatalGlobalErrors === true;
244
+ if (shouldShow) this.setState({ hasError: true });
245
+ }
246
+ render() {
247
+ if (this.state.hasError) return this.props.fallback ?? null;
248
+ return this.props.children;
249
+ }
250
+ };
251
+ function SunglassesGlobalErrorBoundary(props) {
252
+ const contextClient = useContext3(SunglassesContext);
253
+ const client = props.client ?? contextClient;
254
+ if (client === null) {
255
+ throw new Error(
256
+ "[SunGlasses] <SunglassesGlobalErrorBoundary> must be inside a <SunglassesProvider> or receive a `client` prop."
257
+ );
258
+ }
259
+ return /* @__PURE__ */ jsx3(GlobalErrorBoundaryInner, { ...props, client });
260
+ }
261
+
199
262
  // src/index.ts
200
- import { SunglassesCore, captureException as captureException3 } from "@drakkar.software/sunglasses-core";
263
+ import {
264
+ SunglassesCore,
265
+ captureException as captureException4,
266
+ patchConsole as patchConsole2,
267
+ publishGlobalError as publishGlobalError2,
268
+ subscribeGlobalError as subscribeGlobalError2
269
+ } from "@drakkar.software/sunglasses-core";
201
270
  export {
202
271
  SunglassesCore,
203
272
  SunglassesErrorBoundary,
273
+ SunglassesGlobalErrorBoundary,
204
274
  SunglassesProvider,
205
- captureException3 as captureException,
275
+ captureException4 as captureException,
206
276
  captureUtmParams,
277
+ patchConsole2 as patchConsole,
278
+ publishGlobalError2 as publishGlobalError,
279
+ subscribeGlobalError2 as subscribeGlobalError,
207
280
  useCapture,
208
281
  useConsentStatus,
209
282
  useScreenTracking,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drakkar.software/sunglasses-react",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "React (web) provider and hooks for SunGlasses event tracking",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -16,7 +16,7 @@
16
16
  "dist"
17
17
  ],
18
18
  "dependencies": {
19
- "@drakkar.software/sunglasses-core": "0.10.0"
19
+ "@drakkar.software/sunglasses-core": "0.12.0"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "react": ">=18.0.0"