@deeptracer/react 0.2.0 → 0.3.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 ADDED
@@ -0,0 +1,224 @@
1
+ # @deeptracer/react
2
+
3
+ [![npm](https://img.shields.io/npm/v/@deeptracer/react?color=blue)](https://www.npmjs.com/package/@deeptracer/react)
4
+
5
+ DeepTracer React integration — provider, error boundary, and hooks for automatic error capture. Re-exports everything from `@deeptracer/browser`.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @deeptracer/react
11
+ ```
12
+
13
+ **Peer dependencies:** `react >=18`, `react-dom >=18`
14
+
15
+ ## Quick Start
16
+
17
+ ```tsx
18
+ import { DeepTracerProvider, DeepTracerErrorBoundary, useLogger } from "@deeptracer/react"
19
+
20
+ function App() {
21
+ return (
22
+ <DeepTracerProvider config={{
23
+ product: "my-app",
24
+ service: "web",
25
+ environment: "production",
26
+ endpoint: "https://deeptracer.example.com",
27
+ apiKey: "dt_live_xxx",
28
+ }}>
29
+ <DeepTracerErrorBoundary fallback={<div>Something went wrong</div>}>
30
+ <MyApp />
31
+ </DeepTracerErrorBoundary>
32
+ </DeepTracerProvider>
33
+ )
34
+ }
35
+
36
+ function MyApp() {
37
+ const logger = useLogger()
38
+
39
+ function handleClick() {
40
+ logger.info("Button clicked", { component: "MyApp" })
41
+ }
42
+
43
+ return <button onClick={handleClick}>Click me</button>
44
+ }
45
+ ```
46
+
47
+ ## API Reference
48
+
49
+ ### `DeepTracerProvider`
50
+
51
+ React context provider that creates and manages a DeepTracer Logger instance. Automatically captures browser global errors (`window.onerror` and `unhandledrejection`).
52
+
53
+ **Props:**
54
+
55
+ | Prop | Type | Default | Description |
56
+ |------|------|---------|-------------|
57
+ | `config` | `LoggerConfig` | — | Logger configuration. Creates a new Logger internally. |
58
+ | `logger` | `Logger` | — | An existing Logger instance to share. Mutually exclusive with `config`. |
59
+ | `captureErrors` | `boolean` | `true` | Automatically capture unhandled window errors. |
60
+ | `children` | `ReactNode` | — | Child components with access to the logger via `useLogger()`. |
61
+
62
+ If neither `config` nor `logger` is provided, the provider reads from environment variables automatically:
63
+
64
+ | Environment Variable | Description |
65
+ |---------------------|-------------|
66
+ | `NEXT_PUBLIC_DEEPTRACER_ENDPOINT` | Ingestion endpoint URL (required) |
67
+ | `NEXT_PUBLIC_DEEPTRACER_API_KEY` | API key (required) |
68
+ | `NEXT_PUBLIC_DEEPTRACER_PRODUCT` | Product name (required) |
69
+ | `NEXT_PUBLIC_DEEPTRACER_SERVICE` | Service name (default: `"web"`) |
70
+ | `NEXT_PUBLIC_DEEPTRACER_ENVIRONMENT` | `"production"` or `"staging"` (default: `"production"`) |
71
+
72
+ ```tsx
73
+ // Explicit config
74
+ <DeepTracerProvider config={{
75
+ product: "my-app",
76
+ service: "web",
77
+ environment: "production",
78
+ endpoint: process.env.NEXT_PUBLIC_DEEPTRACER_ENDPOINT!,
79
+ apiKey: process.env.NEXT_PUBLIC_DEEPTRACER_API_KEY!,
80
+ }}>
81
+ {children}
82
+ </DeepTracerProvider>
83
+
84
+ // Zero-config (reads NEXT_PUBLIC_DEEPTRACER_* env vars)
85
+ <DeepTracerProvider>{children}</DeepTracerProvider>
86
+
87
+ // Existing logger instance
88
+ <DeepTracerProvider logger={myLogger}>{children}</DeepTracerProvider>
89
+ ```
90
+
91
+ ---
92
+
93
+ ### `DeepTracerErrorPage`
94
+
95
+ Drop-in function component for Next.js `error.tsx` or `global-error.tsx`. Receives `{ error, reset }` from Next.js, calls `captureError()` in `useEffect`, and shows a default fallback UI with a "Try again" button.
96
+
97
+ **One-line setup:**
98
+
99
+ ```tsx
100
+ // app/global-error.tsx
101
+ "use client"
102
+ export { DeepTracerErrorPage as default } from "@deeptracer/react"
103
+ ```
104
+
105
+ ```tsx
106
+ // app/error.tsx
107
+ "use client"
108
+ export { DeepTracerErrorPage as default } from "@deeptracer/react"
109
+ ```
110
+
111
+ Requires a `<DeepTracerProvider>` in the component tree. If no provider is found, the error is displayed but not reported (a `console.warn` is emitted).
112
+
113
+ ---
114
+
115
+ ### `useDeepTracerErrorReporter(error, severity?)`
116
+
117
+ Hook for custom error pages that still want automatic error reporting. Use when you want your own UI but still want DeepTracer to capture the error.
118
+
119
+ **Parameters:**
120
+ - `error: Error` — the error to report
121
+ - `severity: "low" | "medium" | "high" | "critical"` — default: `"high"`
122
+
123
+ ```tsx
124
+ // app/error.tsx — custom UI with automatic reporting
125
+ "use client"
126
+ import { useDeepTracerErrorReporter } from "@deeptracer/react"
127
+
128
+ export default function ErrorPage({ error, reset }: { error: Error; reset: () => void }) {
129
+ useDeepTracerErrorReporter(error)
130
+ return (
131
+ <div>
132
+ <h2>Oops! {error.message}</h2>
133
+ <button onClick={reset}>Try again</button>
134
+ </div>
135
+ )
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ### `DeepTracerErrorBoundary`
142
+
143
+ Class-based React error boundary that catches rendering errors in child components and reports them to DeepTracer.
144
+
145
+ **Props:**
146
+
147
+ | Prop | Type | Description |
148
+ |------|------|-------------|
149
+ | `fallback` | `ReactNode \| (({ error, resetErrorBoundary }) => ReactNode)` | Content to show on error. |
150
+ | `children` | `ReactNode` | Components to protect. |
151
+ | `onError` | `(error: Error, errorInfo: ErrorInfo) => void` | Called after the error is caught and reported. |
152
+
153
+ ```tsx
154
+ import { DeepTracerErrorBoundary } from "@deeptracer/react"
155
+
156
+ // Static fallback
157
+ <DeepTracerErrorBoundary fallback={<div>Something went wrong</div>}>
158
+ <MyComponent />
159
+ </DeepTracerErrorBoundary>
160
+
161
+ // Render function fallback
162
+ <DeepTracerErrorBoundary
163
+ fallback={({ error, resetErrorBoundary }) => (
164
+ <div>
165
+ <p>Error: {error.message}</p>
166
+ <button onClick={resetErrorBoundary}>Retry</button>
167
+ </div>
168
+ )}
169
+ >
170
+ <MyComponent />
171
+ </DeepTracerErrorBoundary>
172
+ ```
173
+
174
+ ---
175
+
176
+ ### `useLogger()`
177
+
178
+ Hook that returns the DeepTracer Logger from context. Throws with a clear error message if used outside a `<DeepTracerProvider>`.
179
+
180
+ ```tsx
181
+ import { useLogger } from "@deeptracer/react"
182
+
183
+ function MyComponent() {
184
+ const logger = useLogger()
185
+
186
+ async function handleSubmit() {
187
+ try {
188
+ await submitData()
189
+ logger.info("Form submitted successfully")
190
+ } catch (error) {
191
+ logger.captureError(error, { severity: "high" })
192
+ }
193
+ }
194
+
195
+ return <button onClick={handleSubmit}>Submit</button>
196
+ }
197
+ ```
198
+
199
+ ## Re-exported from @deeptracer/browser
200
+
201
+ All exports from `@deeptracer/browser` and `@deeptracer/core` are available directly from this package. You do not need to install them separately.
202
+
203
+ ## Monorepo
204
+
205
+ This package is part of the [DeepTracer JavaScript SDK](https://github.com/getdeeptracer/deeptracer-js) monorepo:
206
+
207
+ | Package | Description |
208
+ |---------|-------------|
209
+ | [`@deeptracer/core`](https://github.com/getdeeptracer/deeptracer-js/tree/main/packages/core) | Zero-dependency shared core |
210
+ | [`@deeptracer/node`](https://github.com/getdeeptracer/deeptracer-js/tree/main/packages/node) | Node.js/Bun SDK |
211
+ | [`@deeptracer/ai`](https://github.com/getdeeptracer/deeptracer-js/tree/main/packages/ai) | AI SDK wrappers |
212
+ | [`@deeptracer/browser`](https://github.com/getdeeptracer/deeptracer-js/tree/main/packages/browser) | Browser SDK |
213
+ | **`@deeptracer/react`** | React integration (this package) |
214
+ | [`@deeptracer/nextjs`](https://github.com/getdeeptracer/deeptracer-js/tree/main/packages/nextjs) | Next.js integration |
215
+
216
+ ## Links
217
+
218
+ - [deeptracer.dev](https://deeptracer.dev) — Homepage
219
+ - [Docs](https://deeptracer.dev/docs) — Documentation
220
+ - [GitHub](https://github.com/getdeeptracer/deeptracer-js) — Source code
221
+
222
+ ## License
223
+
224
+ MIT
package/dist/index.cjs CHANGED
@@ -1,8 +1,13 @@
1
+ "use client";
1
2
  "use strict";
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
6
11
  var __copyProps = (to, from, except, desc) => {
7
12
  if (from && typeof from === "object" || typeof from === "function") {
8
13
  for (let key of __getOwnPropNames(from))
@@ -16,9 +21,202 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
16
21
 
17
22
  // src/index.ts
18
23
  var index_exports = {};
24
+ __export(index_exports, {
25
+ DeepTracerErrorBoundary: () => DeepTracerErrorBoundary,
26
+ DeepTracerErrorPage: () => DeepTracerErrorPage,
27
+ DeepTracerProvider: () => DeepTracerProvider,
28
+ useDeepTracerErrorReporter: () => useDeepTracerErrorReporter,
29
+ useLogger: () => useLogger
30
+ });
19
31
  module.exports = __toCommonJS(index_exports);
20
32
  __reExport(index_exports, require("@deeptracer/browser"), module.exports);
33
+
34
+ // src/context.tsx
35
+ var import_react = require("react");
36
+ var import_browser = require("@deeptracer/browser");
37
+ var import_browser2 = require("@deeptracer/browser");
38
+ var import_jsx_runtime = require("react/jsx-runtime");
39
+ var DeepTracerContext = (0, import_react.createContext)(null);
40
+ function DeepTracerProvider({
41
+ config,
42
+ logger: externalLogger,
43
+ captureErrors = true,
44
+ children
45
+ }) {
46
+ const [logger, setLogger] = (0, import_react.useState)(externalLogger ?? null);
47
+ const ownsLogger = (0, import_react.useRef)(false);
48
+ (0, import_react.useEffect)(() => {
49
+ if (externalLogger) {
50
+ setLogger(externalLogger);
51
+ ownsLogger.current = false;
52
+ return;
53
+ }
54
+ const resolvedConfig = config ?? readConfigFromEnv();
55
+ if (!resolvedConfig) {
56
+ console.warn(
57
+ "[@deeptracer/react] DeepTracerProvider: No config, logger, or NEXT_PUBLIC_DEEPTRACER_* env vars found. Logger is disabled. Pass a config prop or set environment variables."
58
+ );
59
+ return;
60
+ }
61
+ const newLogger = (0, import_browser.createLogger)(resolvedConfig);
62
+ setLogger(newLogger);
63
+ ownsLogger.current = true;
64
+ if (captureErrors) {
65
+ (0, import_browser2.captureGlobalErrors)(newLogger);
66
+ }
67
+ return () => {
68
+ newLogger.destroy().catch(() => {
69
+ });
70
+ };
71
+ }, []);
72
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeepTracerContext.Provider, { value: logger, children });
73
+ }
74
+ function readConfigFromEnv() {
75
+ const endpoint = typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_DEEPTRACER_ENDPOINT : void 0;
76
+ const apiKey = typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_DEEPTRACER_API_KEY : void 0;
77
+ const product = typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_DEEPTRACER_PRODUCT : void 0;
78
+ if (!endpoint || !apiKey || !product) return null;
79
+ const service = (typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_DEEPTRACER_SERVICE : void 0) ?? "web";
80
+ const environment = (typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_DEEPTRACER_ENVIRONMENT : void 0) ?? "production";
81
+ return { product, service, environment, endpoint, apiKey };
82
+ }
83
+
84
+ // src/error-boundary.tsx
85
+ var import_react2 = require("react");
86
+ var import_jsx_runtime2 = require("react/jsx-runtime");
87
+ function DeepTracerErrorPage({
88
+ error,
89
+ reset
90
+ }) {
91
+ const logger = (0, import_react2.useContext)(DeepTracerContext);
92
+ (0, import_react2.useEffect)(() => {
93
+ if (logger) {
94
+ logger.captureError(error, {
95
+ severity: "high",
96
+ context: {
97
+ source: "react-error-page",
98
+ digest: error.digest
99
+ }
100
+ });
101
+ } else {
102
+ console.warn(
103
+ "[@deeptracer/react] DeepTracerErrorPage: No DeepTracerProvider found. Error not reported to DeepTracer. Wrap your app with <DeepTracerProvider>."
104
+ );
105
+ }
106
+ }, [error, logger]);
107
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { padding: "2rem", fontFamily: "system-ui, sans-serif" }, children: [
108
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { style: { marginBottom: "1rem" }, children: "Something went wrong" }),
109
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { color: "#666", marginBottom: "1.5rem" }, children: error.message || "An unexpected error occurred." }),
110
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
111
+ "button",
112
+ {
113
+ onClick: reset,
114
+ style: {
115
+ padding: "0.5rem 1rem",
116
+ borderRadius: "0.375rem",
117
+ border: "1px solid #ccc",
118
+ background: "#fff",
119
+ cursor: "pointer",
120
+ fontSize: "0.875rem"
121
+ },
122
+ children: "Try again"
123
+ }
124
+ )
125
+ ] });
126
+ }
127
+ function useDeepTracerErrorReporter(error, severity = "high") {
128
+ const logger = (0, import_react2.useContext)(DeepTracerContext);
129
+ (0, import_react2.useEffect)(() => {
130
+ if (logger) {
131
+ logger.captureError(error, {
132
+ severity,
133
+ context: { source: "react-error-page" }
134
+ });
135
+ }
136
+ }, [error, logger, severity]);
137
+ }
138
+ var DeepTracerErrorBoundary = class extends import_react2.Component {
139
+ static contextType = DeepTracerContext;
140
+ constructor(props) {
141
+ super(props);
142
+ this.state = { hasError: false, error: null };
143
+ }
144
+ static getDerivedStateFromError(error) {
145
+ return { hasError: true, error };
146
+ }
147
+ componentDidCatch(error, errorInfo) {
148
+ const logger = this.context;
149
+ if (logger) {
150
+ logger.captureError(error, {
151
+ severity: "high",
152
+ context: {
153
+ source: "react-error-boundary",
154
+ componentStack: errorInfo.componentStack ?? void 0
155
+ }
156
+ });
157
+ } else {
158
+ console.warn(
159
+ "[@deeptracer/react] DeepTracerErrorBoundary: No DeepTracerProvider found. Error not reported to DeepTracer."
160
+ );
161
+ }
162
+ this.props.onError?.(error, errorInfo);
163
+ }
164
+ resetErrorBoundary = () => {
165
+ this.setState({ hasError: false, error: null });
166
+ };
167
+ render() {
168
+ if (this.state.hasError && this.state.error) {
169
+ const { fallback } = this.props;
170
+ if (typeof fallback === "function") {
171
+ return fallback({
172
+ error: this.state.error,
173
+ resetErrorBoundary: this.resetErrorBoundary
174
+ });
175
+ }
176
+ if (fallback) {
177
+ return fallback;
178
+ }
179
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { padding: "2rem", fontFamily: "system-ui, sans-serif" }, children: [
180
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { style: { marginBottom: "1rem" }, children: "Something went wrong" }),
181
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { color: "#666", marginBottom: "1.5rem" }, children: this.state.error.message || "An unexpected error occurred." }),
182
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
183
+ "button",
184
+ {
185
+ onClick: this.resetErrorBoundary,
186
+ style: {
187
+ padding: "0.5rem 1rem",
188
+ borderRadius: "0.375rem",
189
+ border: "1px solid #ccc",
190
+ background: "#fff",
191
+ cursor: "pointer",
192
+ fontSize: "0.875rem"
193
+ },
194
+ children: "Try again"
195
+ }
196
+ )
197
+ ] });
198
+ }
199
+ return this.props.children;
200
+ }
201
+ };
202
+
203
+ // src/hooks.ts
204
+ var import_react3 = require("react");
205
+ function useLogger() {
206
+ const logger = (0, import_react3.useContext)(DeepTracerContext);
207
+ if (!logger) {
208
+ throw new Error(
209
+ "[@deeptracer/react] useLogger() must be used inside a <DeepTracerProvider>. Wrap your app with <DeepTracerProvider config={{...}}>."
210
+ );
211
+ }
212
+ return logger;
213
+ }
21
214
  // Annotate the CommonJS export names for ESM import in node:
22
215
  0 && (module.exports = {
216
+ DeepTracerErrorBoundary,
217
+ DeepTracerErrorPage,
218
+ DeepTracerProvider,
219
+ useDeepTracerErrorReporter,
220
+ useLogger,
23
221
  ...require("@deeptracer/browser")
24
222
  });
package/dist/index.d.cts CHANGED
@@ -1 +1,232 @@
1
+ import { LoggerConfig, Logger } from '@deeptracer/browser';
1
2
  export * from '@deeptracer/browser';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import * as react from 'react';
5
+ import { ReactNode, Component, ErrorInfo } from 'react';
6
+
7
+ /**
8
+ * Props for the DeepTracerProvider component.
9
+ *
10
+ * Pass either a `config` object to create a new Logger, or an existing
11
+ * `logger` instance. If neither is given, reads from `NEXT_PUBLIC_DEEPTRACER_*`
12
+ * environment variables automatically (zero-config for Next.js).
13
+ */
14
+ interface DeepTracerProviderProps {
15
+ /**
16
+ * Logger configuration. A new Logger is created internally.
17
+ * Mutually exclusive with `logger`.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * <DeepTracerProvider config={{
22
+ * product: "my-app",
23
+ * service: "web",
24
+ * environment: "production",
25
+ * endpoint: process.env.NEXT_PUBLIC_DEEPTRACER_ENDPOINT!,
26
+ * apiKey: process.env.NEXT_PUBLIC_DEEPTRACER_API_KEY!,
27
+ * }}>
28
+ * ```
29
+ */
30
+ config?: LoggerConfig;
31
+ /**
32
+ * An existing Logger instance to share with child components.
33
+ * Mutually exclusive with `config`.
34
+ */
35
+ logger?: Logger;
36
+ /**
37
+ * Automatically capture unhandled window errors and promise rejections.
38
+ * Default: true
39
+ */
40
+ captureErrors?: boolean;
41
+ /** Child components that will have access to the logger via useLogger(). */
42
+ children: ReactNode;
43
+ }
44
+ /**
45
+ * Provides a DeepTracer Logger to all child components via React context.
46
+ *
47
+ * Automatically captures browser global errors (window.onerror and
48
+ * unhandledrejection) unless `captureErrors={false}`.
49
+ *
50
+ * The Logger is created once on mount and destroyed on unmount.
51
+ *
52
+ * @example
53
+ * With explicit config:
54
+ * ```tsx
55
+ * import { DeepTracerProvider } from "@deeptracer/react"
56
+ *
57
+ * export default function App({ children }) {
58
+ * return (
59
+ * <DeepTracerProvider config={{
60
+ * product: "my-app",
61
+ * service: "web",
62
+ * environment: "production",
63
+ * endpoint: "https://deeptracer.example.com",
64
+ * apiKey: process.env.NEXT_PUBLIC_DEEPTRACER_API_KEY!,
65
+ * }}>
66
+ * {children}
67
+ * </DeepTracerProvider>
68
+ * )
69
+ * }
70
+ * ```
71
+ *
72
+ * @example
73
+ * Zero-config (reads NEXT_PUBLIC_DEEPTRACER_* env vars):
74
+ * ```tsx
75
+ * <DeepTracerProvider>{children}</DeepTracerProvider>
76
+ * ```
77
+ */
78
+ declare function DeepTracerProvider({ config, logger: externalLogger, captureErrors, children, }: DeepTracerProviderProps): react_jsx_runtime.JSX.Element;
79
+
80
+ /**
81
+ * Drop-in error page component for Next.js `error.tsx` or `global-error.tsx`.
82
+ *
83
+ * Automatically reports the error to DeepTracer when a `<DeepTracerProvider>`
84
+ * is present in the component tree. If no provider is found, the error is
85
+ * still displayed but not reported (a console.warn is emitted).
86
+ *
87
+ * @example
88
+ * One-line setup for `app/global-error.tsx`:
89
+ * ```tsx
90
+ * "use client"
91
+ * export { DeepTracerErrorPage as default } from "@deeptracer/react"
92
+ * ```
93
+ *
94
+ * @example
95
+ * One-line setup for `app/error.tsx`:
96
+ * ```tsx
97
+ * "use client"
98
+ * export { DeepTracerErrorPage as default } from "@deeptracer/react"
99
+ * ```
100
+ */
101
+ declare function DeepTracerErrorPage({ error, reset, }: {
102
+ error: Error & {
103
+ digest?: string;
104
+ };
105
+ reset: () => void;
106
+ }): react_jsx_runtime.JSX.Element;
107
+ /**
108
+ * Hook that reports an error to DeepTracer. Use in custom `error.tsx` pages
109
+ * when you want your own UI but still want automatic error reporting.
110
+ *
111
+ * @param error - The error to report
112
+ * @param severity - Error severity (default: "high")
113
+ *
114
+ * @example
115
+ * ```tsx
116
+ * // app/error.tsx
117
+ * "use client"
118
+ * import { useDeepTracerErrorReporter } from "@deeptracer/react"
119
+ *
120
+ * export default function ErrorPage({ error, reset }) {
121
+ * useDeepTracerErrorReporter(error)
122
+ * return <div>Custom error UI <button onClick={reset}>Retry</button></div>
123
+ * }
124
+ * ```
125
+ */
126
+ declare function useDeepTracerErrorReporter(error: Error, severity?: "low" | "medium" | "high" | "critical"): void;
127
+ /**
128
+ * Props for the DeepTracerErrorBoundary component.
129
+ */
130
+ interface ErrorBoundaryProps {
131
+ /** Content to render when an error occurs. Static ReactNode or render function. */
132
+ fallback?: ReactNode | ((props: {
133
+ error: Error;
134
+ resetErrorBoundary: () => void;
135
+ }) => ReactNode);
136
+ /** Child components to protect with the error boundary. */
137
+ children: ReactNode;
138
+ /** Called when an error is caught, after reporting to DeepTracer. */
139
+ onError?: (error: Error, errorInfo: ErrorInfo) => void;
140
+ }
141
+ interface ErrorBoundaryState {
142
+ hasError: boolean;
143
+ error: Error | null;
144
+ }
145
+ /**
146
+ * Error boundary component that catches React rendering errors in child
147
+ * components and automatically reports them to DeepTracer.
148
+ *
149
+ * For Next.js `error.tsx` / `global-error.tsx`, use `DeepTracerErrorPage` instead.
150
+ * This component is for wrapping arbitrary React trees.
151
+ *
152
+ * @example
153
+ * ```tsx
154
+ * import { DeepTracerErrorBoundary } from "@deeptracer/react"
155
+ *
156
+ * function App() {
157
+ * return (
158
+ * <DeepTracerErrorBoundary fallback={<div>Something went wrong</div>}>
159
+ * <MyComponent />
160
+ * </DeepTracerErrorBoundary>
161
+ * )
162
+ * }
163
+ * ```
164
+ *
165
+ * @example
166
+ * With render function fallback:
167
+ * ```tsx
168
+ * <DeepTracerErrorBoundary
169
+ * fallback={({ error, resetErrorBoundary }) => (
170
+ * <div>
171
+ * <p>Error: {error.message}</p>
172
+ * <button onClick={resetErrorBoundary}>Retry</button>
173
+ * </div>
174
+ * )}
175
+ * >
176
+ * <MyComponent />
177
+ * </DeepTracerErrorBoundary>
178
+ * ```
179
+ */
180
+ declare class DeepTracerErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
181
+ static contextType: react.Context<Logger | null>;
182
+ context: Logger | null;
183
+ constructor(props: ErrorBoundaryProps);
184
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState;
185
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
186
+ resetErrorBoundary: () => void;
187
+ render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null | undefined;
188
+ }
189
+
190
+ /**
191
+ * Access the DeepTracer Logger instance from React context.
192
+ *
193
+ * Must be used inside a `<DeepTracerProvider>`. Throws with a clear
194
+ * error message if no provider is found.
195
+ *
196
+ * @returns The Logger instance from the nearest DeepTracerProvider
197
+ * @throws Error if called outside a DeepTracerProvider
198
+ *
199
+ * @example
200
+ * ```tsx
201
+ * import { useLogger } from "@deeptracer/react"
202
+ *
203
+ * function MyComponent() {
204
+ * const logger = useLogger()
205
+ *
206
+ * function handleClick() {
207
+ * logger.info("Button clicked", { component: "MyComponent" })
208
+ * }
209
+ *
210
+ * return <button onClick={handleClick}>Click me</button>
211
+ * }
212
+ * ```
213
+ *
214
+ * @example
215
+ * Capture errors manually:
216
+ * ```tsx
217
+ * function SubmitForm() {
218
+ * const logger = useLogger()
219
+ *
220
+ * async function handleSubmit() {
221
+ * try {
222
+ * await submitData()
223
+ * } catch (error) {
224
+ * logger.captureError(error, { severity: "high" })
225
+ * }
226
+ * }
227
+ * }
228
+ * ```
229
+ */
230
+ declare function useLogger(): Logger;
231
+
232
+ export { DeepTracerErrorBoundary, DeepTracerErrorPage, DeepTracerProvider, type DeepTracerProviderProps, useDeepTracerErrorReporter, useLogger };
package/dist/index.d.ts CHANGED
@@ -1 +1,232 @@
1
+ import { LoggerConfig, Logger } from '@deeptracer/browser';
1
2
  export * from '@deeptracer/browser';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import * as react from 'react';
5
+ import { ReactNode, Component, ErrorInfo } from 'react';
6
+
7
+ /**
8
+ * Props for the DeepTracerProvider component.
9
+ *
10
+ * Pass either a `config` object to create a new Logger, or an existing
11
+ * `logger` instance. If neither is given, reads from `NEXT_PUBLIC_DEEPTRACER_*`
12
+ * environment variables automatically (zero-config for Next.js).
13
+ */
14
+ interface DeepTracerProviderProps {
15
+ /**
16
+ * Logger configuration. A new Logger is created internally.
17
+ * Mutually exclusive with `logger`.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * <DeepTracerProvider config={{
22
+ * product: "my-app",
23
+ * service: "web",
24
+ * environment: "production",
25
+ * endpoint: process.env.NEXT_PUBLIC_DEEPTRACER_ENDPOINT!,
26
+ * apiKey: process.env.NEXT_PUBLIC_DEEPTRACER_API_KEY!,
27
+ * }}>
28
+ * ```
29
+ */
30
+ config?: LoggerConfig;
31
+ /**
32
+ * An existing Logger instance to share with child components.
33
+ * Mutually exclusive with `config`.
34
+ */
35
+ logger?: Logger;
36
+ /**
37
+ * Automatically capture unhandled window errors and promise rejections.
38
+ * Default: true
39
+ */
40
+ captureErrors?: boolean;
41
+ /** Child components that will have access to the logger via useLogger(). */
42
+ children: ReactNode;
43
+ }
44
+ /**
45
+ * Provides a DeepTracer Logger to all child components via React context.
46
+ *
47
+ * Automatically captures browser global errors (window.onerror and
48
+ * unhandledrejection) unless `captureErrors={false}`.
49
+ *
50
+ * The Logger is created once on mount and destroyed on unmount.
51
+ *
52
+ * @example
53
+ * With explicit config:
54
+ * ```tsx
55
+ * import { DeepTracerProvider } from "@deeptracer/react"
56
+ *
57
+ * export default function App({ children }) {
58
+ * return (
59
+ * <DeepTracerProvider config={{
60
+ * product: "my-app",
61
+ * service: "web",
62
+ * environment: "production",
63
+ * endpoint: "https://deeptracer.example.com",
64
+ * apiKey: process.env.NEXT_PUBLIC_DEEPTRACER_API_KEY!,
65
+ * }}>
66
+ * {children}
67
+ * </DeepTracerProvider>
68
+ * )
69
+ * }
70
+ * ```
71
+ *
72
+ * @example
73
+ * Zero-config (reads NEXT_PUBLIC_DEEPTRACER_* env vars):
74
+ * ```tsx
75
+ * <DeepTracerProvider>{children}</DeepTracerProvider>
76
+ * ```
77
+ */
78
+ declare function DeepTracerProvider({ config, logger: externalLogger, captureErrors, children, }: DeepTracerProviderProps): react_jsx_runtime.JSX.Element;
79
+
80
+ /**
81
+ * Drop-in error page component for Next.js `error.tsx` or `global-error.tsx`.
82
+ *
83
+ * Automatically reports the error to DeepTracer when a `<DeepTracerProvider>`
84
+ * is present in the component tree. If no provider is found, the error is
85
+ * still displayed but not reported (a console.warn is emitted).
86
+ *
87
+ * @example
88
+ * One-line setup for `app/global-error.tsx`:
89
+ * ```tsx
90
+ * "use client"
91
+ * export { DeepTracerErrorPage as default } from "@deeptracer/react"
92
+ * ```
93
+ *
94
+ * @example
95
+ * One-line setup for `app/error.tsx`:
96
+ * ```tsx
97
+ * "use client"
98
+ * export { DeepTracerErrorPage as default } from "@deeptracer/react"
99
+ * ```
100
+ */
101
+ declare function DeepTracerErrorPage({ error, reset, }: {
102
+ error: Error & {
103
+ digest?: string;
104
+ };
105
+ reset: () => void;
106
+ }): react_jsx_runtime.JSX.Element;
107
+ /**
108
+ * Hook that reports an error to DeepTracer. Use in custom `error.tsx` pages
109
+ * when you want your own UI but still want automatic error reporting.
110
+ *
111
+ * @param error - The error to report
112
+ * @param severity - Error severity (default: "high")
113
+ *
114
+ * @example
115
+ * ```tsx
116
+ * // app/error.tsx
117
+ * "use client"
118
+ * import { useDeepTracerErrorReporter } from "@deeptracer/react"
119
+ *
120
+ * export default function ErrorPage({ error, reset }) {
121
+ * useDeepTracerErrorReporter(error)
122
+ * return <div>Custom error UI <button onClick={reset}>Retry</button></div>
123
+ * }
124
+ * ```
125
+ */
126
+ declare function useDeepTracerErrorReporter(error: Error, severity?: "low" | "medium" | "high" | "critical"): void;
127
+ /**
128
+ * Props for the DeepTracerErrorBoundary component.
129
+ */
130
+ interface ErrorBoundaryProps {
131
+ /** Content to render when an error occurs. Static ReactNode or render function. */
132
+ fallback?: ReactNode | ((props: {
133
+ error: Error;
134
+ resetErrorBoundary: () => void;
135
+ }) => ReactNode);
136
+ /** Child components to protect with the error boundary. */
137
+ children: ReactNode;
138
+ /** Called when an error is caught, after reporting to DeepTracer. */
139
+ onError?: (error: Error, errorInfo: ErrorInfo) => void;
140
+ }
141
+ interface ErrorBoundaryState {
142
+ hasError: boolean;
143
+ error: Error | null;
144
+ }
145
+ /**
146
+ * Error boundary component that catches React rendering errors in child
147
+ * components and automatically reports them to DeepTracer.
148
+ *
149
+ * For Next.js `error.tsx` / `global-error.tsx`, use `DeepTracerErrorPage` instead.
150
+ * This component is for wrapping arbitrary React trees.
151
+ *
152
+ * @example
153
+ * ```tsx
154
+ * import { DeepTracerErrorBoundary } from "@deeptracer/react"
155
+ *
156
+ * function App() {
157
+ * return (
158
+ * <DeepTracerErrorBoundary fallback={<div>Something went wrong</div>}>
159
+ * <MyComponent />
160
+ * </DeepTracerErrorBoundary>
161
+ * )
162
+ * }
163
+ * ```
164
+ *
165
+ * @example
166
+ * With render function fallback:
167
+ * ```tsx
168
+ * <DeepTracerErrorBoundary
169
+ * fallback={({ error, resetErrorBoundary }) => (
170
+ * <div>
171
+ * <p>Error: {error.message}</p>
172
+ * <button onClick={resetErrorBoundary}>Retry</button>
173
+ * </div>
174
+ * )}
175
+ * >
176
+ * <MyComponent />
177
+ * </DeepTracerErrorBoundary>
178
+ * ```
179
+ */
180
+ declare class DeepTracerErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
181
+ static contextType: react.Context<Logger | null>;
182
+ context: Logger | null;
183
+ constructor(props: ErrorBoundaryProps);
184
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState;
185
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
186
+ resetErrorBoundary: () => void;
187
+ render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null | undefined;
188
+ }
189
+
190
+ /**
191
+ * Access the DeepTracer Logger instance from React context.
192
+ *
193
+ * Must be used inside a `<DeepTracerProvider>`. Throws with a clear
194
+ * error message if no provider is found.
195
+ *
196
+ * @returns The Logger instance from the nearest DeepTracerProvider
197
+ * @throws Error if called outside a DeepTracerProvider
198
+ *
199
+ * @example
200
+ * ```tsx
201
+ * import { useLogger } from "@deeptracer/react"
202
+ *
203
+ * function MyComponent() {
204
+ * const logger = useLogger()
205
+ *
206
+ * function handleClick() {
207
+ * logger.info("Button clicked", { component: "MyComponent" })
208
+ * }
209
+ *
210
+ * return <button onClick={handleClick}>Click me</button>
211
+ * }
212
+ * ```
213
+ *
214
+ * @example
215
+ * Capture errors manually:
216
+ * ```tsx
217
+ * function SubmitForm() {
218
+ * const logger = useLogger()
219
+ *
220
+ * async function handleSubmit() {
221
+ * try {
222
+ * await submitData()
223
+ * } catch (error) {
224
+ * logger.captureError(error, { severity: "high" })
225
+ * }
226
+ * }
227
+ * }
228
+ * ```
229
+ */
230
+ declare function useLogger(): Logger;
231
+
232
+ export { DeepTracerErrorBoundary, DeepTracerErrorPage, DeepTracerProvider, type DeepTracerProviderProps, useDeepTracerErrorReporter, useLogger };
package/dist/index.js CHANGED
@@ -1,2 +1,192 @@
1
+ "use client";
2
+
1
3
  // src/index.ts
2
4
  export * from "@deeptracer/browser";
5
+
6
+ // src/context.tsx
7
+ import { createContext, useEffect, useRef, useState } from "react";
8
+ import { createLogger } from "@deeptracer/browser";
9
+ import { captureGlobalErrors } from "@deeptracer/browser";
10
+ import { jsx } from "react/jsx-runtime";
11
+ var DeepTracerContext = createContext(null);
12
+ function DeepTracerProvider({
13
+ config,
14
+ logger: externalLogger,
15
+ captureErrors = true,
16
+ children
17
+ }) {
18
+ const [logger, setLogger] = useState(externalLogger ?? null);
19
+ const ownsLogger = useRef(false);
20
+ useEffect(() => {
21
+ if (externalLogger) {
22
+ setLogger(externalLogger);
23
+ ownsLogger.current = false;
24
+ return;
25
+ }
26
+ const resolvedConfig = config ?? readConfigFromEnv();
27
+ if (!resolvedConfig) {
28
+ console.warn(
29
+ "[@deeptracer/react] DeepTracerProvider: No config, logger, or NEXT_PUBLIC_DEEPTRACER_* env vars found. Logger is disabled. Pass a config prop or set environment variables."
30
+ );
31
+ return;
32
+ }
33
+ const newLogger = createLogger(resolvedConfig);
34
+ setLogger(newLogger);
35
+ ownsLogger.current = true;
36
+ if (captureErrors) {
37
+ captureGlobalErrors(newLogger);
38
+ }
39
+ return () => {
40
+ newLogger.destroy().catch(() => {
41
+ });
42
+ };
43
+ }, []);
44
+ return /* @__PURE__ */ jsx(DeepTracerContext.Provider, { value: logger, children });
45
+ }
46
+ function readConfigFromEnv() {
47
+ const endpoint = typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_DEEPTRACER_ENDPOINT : void 0;
48
+ const apiKey = typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_DEEPTRACER_API_KEY : void 0;
49
+ const product = typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_DEEPTRACER_PRODUCT : void 0;
50
+ if (!endpoint || !apiKey || !product) return null;
51
+ const service = (typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_DEEPTRACER_SERVICE : void 0) ?? "web";
52
+ const environment = (typeof process !== "undefined" ? process.env?.NEXT_PUBLIC_DEEPTRACER_ENVIRONMENT : void 0) ?? "production";
53
+ return { product, service, environment, endpoint, apiKey };
54
+ }
55
+
56
+ // src/error-boundary.tsx
57
+ import { Component, useContext, useEffect as useEffect2 } from "react";
58
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
59
+ function DeepTracerErrorPage({
60
+ error,
61
+ reset
62
+ }) {
63
+ const logger = useContext(DeepTracerContext);
64
+ useEffect2(() => {
65
+ if (logger) {
66
+ logger.captureError(error, {
67
+ severity: "high",
68
+ context: {
69
+ source: "react-error-page",
70
+ digest: error.digest
71
+ }
72
+ });
73
+ } else {
74
+ console.warn(
75
+ "[@deeptracer/react] DeepTracerErrorPage: No DeepTracerProvider found. Error not reported to DeepTracer. Wrap your app with <DeepTracerProvider>."
76
+ );
77
+ }
78
+ }, [error, logger]);
79
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "2rem", fontFamily: "system-ui, sans-serif" }, children: [
80
+ /* @__PURE__ */ jsx2("h2", { style: { marginBottom: "1rem" }, children: "Something went wrong" }),
81
+ /* @__PURE__ */ jsx2("p", { style: { color: "#666", marginBottom: "1.5rem" }, children: error.message || "An unexpected error occurred." }),
82
+ /* @__PURE__ */ jsx2(
83
+ "button",
84
+ {
85
+ onClick: reset,
86
+ style: {
87
+ padding: "0.5rem 1rem",
88
+ borderRadius: "0.375rem",
89
+ border: "1px solid #ccc",
90
+ background: "#fff",
91
+ cursor: "pointer",
92
+ fontSize: "0.875rem"
93
+ },
94
+ children: "Try again"
95
+ }
96
+ )
97
+ ] });
98
+ }
99
+ function useDeepTracerErrorReporter(error, severity = "high") {
100
+ const logger = useContext(DeepTracerContext);
101
+ useEffect2(() => {
102
+ if (logger) {
103
+ logger.captureError(error, {
104
+ severity,
105
+ context: { source: "react-error-page" }
106
+ });
107
+ }
108
+ }, [error, logger, severity]);
109
+ }
110
+ var DeepTracerErrorBoundary = class extends Component {
111
+ static contextType = DeepTracerContext;
112
+ constructor(props) {
113
+ super(props);
114
+ this.state = { hasError: false, error: null };
115
+ }
116
+ static getDerivedStateFromError(error) {
117
+ return { hasError: true, error };
118
+ }
119
+ componentDidCatch(error, errorInfo) {
120
+ const logger = this.context;
121
+ if (logger) {
122
+ logger.captureError(error, {
123
+ severity: "high",
124
+ context: {
125
+ source: "react-error-boundary",
126
+ componentStack: errorInfo.componentStack ?? void 0
127
+ }
128
+ });
129
+ } else {
130
+ console.warn(
131
+ "[@deeptracer/react] DeepTracerErrorBoundary: No DeepTracerProvider found. Error not reported to DeepTracer."
132
+ );
133
+ }
134
+ this.props.onError?.(error, errorInfo);
135
+ }
136
+ resetErrorBoundary = () => {
137
+ this.setState({ hasError: false, error: null });
138
+ };
139
+ render() {
140
+ if (this.state.hasError && this.state.error) {
141
+ const { fallback } = this.props;
142
+ if (typeof fallback === "function") {
143
+ return fallback({
144
+ error: this.state.error,
145
+ resetErrorBoundary: this.resetErrorBoundary
146
+ });
147
+ }
148
+ if (fallback) {
149
+ return fallback;
150
+ }
151
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "2rem", fontFamily: "system-ui, sans-serif" }, children: [
152
+ /* @__PURE__ */ jsx2("h2", { style: { marginBottom: "1rem" }, children: "Something went wrong" }),
153
+ /* @__PURE__ */ jsx2("p", { style: { color: "#666", marginBottom: "1.5rem" }, children: this.state.error.message || "An unexpected error occurred." }),
154
+ /* @__PURE__ */ jsx2(
155
+ "button",
156
+ {
157
+ onClick: this.resetErrorBoundary,
158
+ style: {
159
+ padding: "0.5rem 1rem",
160
+ borderRadius: "0.375rem",
161
+ border: "1px solid #ccc",
162
+ background: "#fff",
163
+ cursor: "pointer",
164
+ fontSize: "0.875rem"
165
+ },
166
+ children: "Try again"
167
+ }
168
+ )
169
+ ] });
170
+ }
171
+ return this.props.children;
172
+ }
173
+ };
174
+
175
+ // src/hooks.ts
176
+ import { useContext as useContext2 } from "react";
177
+ function useLogger() {
178
+ const logger = useContext2(DeepTracerContext);
179
+ if (!logger) {
180
+ throw new Error(
181
+ "[@deeptracer/react] useLogger() must be used inside a <DeepTracerProvider>. Wrap your app with <DeepTracerProvider config={{...}}>."
182
+ );
183
+ }
184
+ return logger;
185
+ }
186
+ export {
187
+ DeepTracerErrorBoundary,
188
+ DeepTracerErrorPage,
189
+ DeepTracerProvider,
190
+ useDeepTracerErrorReporter,
191
+ useLogger
192
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deeptracer/react",
3
- "version": "0.2.0",
4
- "description": "DeepTracer React integration — error boundaries and hooks (coming soon)",
3
+ "version": "0.3.1",
4
+ "description": "DeepTracer React integration — provider, error boundary, and hooks for automatic error capture",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.js",
@@ -13,9 +13,18 @@
13
13
  "require": "./dist/index.cjs"
14
14
  }
15
15
  },
16
- "files": ["dist", "README.md"],
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
17
20
  "sideEffects": false,
18
- "keywords": ["deeptracer", "react", "error-boundary"],
21
+ "keywords": [
22
+ "deeptracer",
23
+ "react",
24
+ "error-boundary",
25
+ "observability",
26
+ "hooks"
27
+ ],
19
28
  "repository": {
20
29
  "type": "git",
21
30
  "url": "https://github.com/getdeeptracer/deeptracer-js.git",
@@ -23,13 +32,18 @@
23
32
  },
24
33
  "license": "MIT",
25
34
  "dependencies": {
26
- "@deeptracer/browser": "0.2.0"
35
+ "@deeptracer/browser": "0.3.1"
27
36
  },
28
37
  "peerDependencies": {
29
- "react": ">=18"
38
+ "react": ">=18",
39
+ "react-dom": ">=18"
30
40
  },
31
41
  "scripts": {
32
42
  "build": "tsup",
33
43
  "dev": "tsup --watch"
44
+ },
45
+ "devDependencies": {
46
+ "@types/react": "^19.2.14",
47
+ "@types/react-dom": "^19.2.3"
34
48
  }
35
49
  }