@debugkit/sdk 0.0.0-stage → 0.2.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 +228 -2
- package/dist/chunk-3WRI3DI2.js +41 -0
- package/dist/chunk-3WRI3DI2.js.map +1 -0
- package/dist/chunk-5C7KJJNO.js +651 -0
- package/dist/chunk-5C7KJJNO.js.map +1 -0
- package/dist/chunk-EGCXLOFH.cjs +663 -0
- package/dist/chunk-EGCXLOFH.cjs.map +1 -0
- package/dist/chunk-HO3JW55K.js +79 -0
- package/dist/chunk-HO3JW55K.js.map +1 -0
- package/dist/chunk-NJ6DTUVX.cjs +83 -0
- package/dist/chunk-NJ6DTUVX.cjs.map +1 -0
- package/dist/chunk-YELZOB7J.cjs +45 -0
- package/dist/chunk-YELZOB7J.cjs.map +1 -0
- package/dist/client-ekzlpWIR.d.cts +220 -0
- package/dist/client-ekzlpWIR.d.ts +220 -0
- package/dist/expo-router.cjs +13 -0
- package/dist/expo-router.cjs.map +1 -0
- package/dist/expo-router.d.cts +10 -0
- package/dist/expo-router.d.ts +10 -0
- package/dist/expo-router.js +11 -0
- package/dist/expo-router.js.map +1 -0
- package/dist/expo.cjs +97 -0
- package/dist/expo.cjs.map +1 -0
- package/dist/expo.d.cts +14 -0
- package/dist/expo.d.ts +14 -0
- package/dist/expo.js +47 -0
- package/dist/expo.js.map +1 -0
- package/dist/index.cjs +40 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/node.cjs +140 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +20 -0
- package/dist/node.d.ts +20 -0
- package/dist/node.js +100 -0
- package/dist/node.js.map +1 -0
- package/dist/react-native.cjs +38 -0
- package/dist/react-native.cjs.map +1 -0
- package/dist/react-native.d.cts +51 -0
- package/dist/react-native.d.ts +51 -0
- package/dist/react-native.js +5 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react.cjs +25 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +50 -0
- package/dist/react.d.ts +50 -0
- package/dist/react.js +4 -0
- package/dist/react.js.map +1 -0
- package/expo/package.json +6 -0
- package/expo-router/package.json +6 -0
- package/node/package.json +6 -0
- package/package.json +121 -3
- package/react/package.json +6 -0
- package/react-native/package.json +6 -0
package/README.md
CHANGED
|
@@ -1,3 +1,229 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @debugkit/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Log errors and events from your app into **your own** cloud. This is the client half of
|
|
4
|
+
[deBug](https://github.com/Abantu-Digital/deBug): events go to an ingest function you deploy
|
|
5
|
+
in Firebase or AWS, and a dashboard you run yourself tells you what is failing and why.
|
|
6
|
+
|
|
7
|
+
- About 6 KB gzipped, with no runtime dependencies in your bundle.
|
|
8
|
+
- Works with React, React Native (bare and Expo), Expo Router, TanStack Start/Router, Next.js
|
|
9
|
+
and plain Node.
|
|
10
|
+
- **Works on its own once initialised:** crashes, failed requests, sessions and the steps
|
|
11
|
+
leading up to each error are recorded without any extra code. See
|
|
12
|
+
[What it records by itself](#what-it-records-by-itself).
|
|
13
|
+
- Batches, retries, rate-limits, and queues events while offline.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @debugkit/sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Pick the entry point for your platform. Each one adds the right device context and error
|
|
22
|
+
hooks, and they all share the same `deBug` client.
|
|
23
|
+
|
|
24
|
+
| You have | Import from | Also install |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| Expo | `@debugkit/sdk/expo` | `npx expo install expo-device expo-application` |
|
|
27
|
+
| Expo Router | `@debugkit/sdk/expo-router`, plus the above | |
|
|
28
|
+
| Bare React Native | `@debugkit/sdk/react-native` | `react-native-device-info` (optional, for model and app version) |
|
|
29
|
+
| React on the web (Vite and others) | `@debugkit/sdk/react` | |
|
|
30
|
+
| TanStack Start / Router | `@debugkit/sdk/react` for the browser, `@debugkit/sdk/node` for the server | |
|
|
31
|
+
| Next.js, Remix, Express, any Node server | `@debugkit/sdk/node` | |
|
|
32
|
+
| Anything else (workers, edge, vanilla JS) | `@debugkit/sdk` | |
|
|
33
|
+
|
|
34
|
+
Your ingest endpoint and key come from your deBug deployment. During development, point at a
|
|
35
|
+
dashboard running in demo mode: `http://localhost:4400/api/ingest` with key `pk_local`.
|
|
36
|
+
|
|
37
|
+
## What it records by itself
|
|
38
|
+
|
|
39
|
+
Calling `init()` (or `initExpo`, `initReactNative`, `initNode`) turns on everything in this
|
|
40
|
+
table. The manual API below is for what only you know: which part of the app a step belongs to,
|
|
41
|
+
and whether it succeeded.
|
|
42
|
+
|
|
43
|
+
| | Browser | React Native / Expo | Node |
|
|
44
|
+
|---|---|---|---|
|
|
45
|
+
| Uncaught errors and unhandled rejections | ✓ | ✓ (fatal crashes marked critical) | ✓ (flushes, then exits like Node) |
|
|
46
|
+
| Render errors | with `DebugErrorBoundary` | with `DebugErrorBoundary` | |
|
|
47
|
+
| **Failed requests** (5xx, timeouts, network failures) as errors, every request as a breadcrumb | fetch, XHR | fetch, XHR (axios too) | fetch, `node:http` (axios, SDKs), plus 5xx your server sends |
|
|
48
|
+
| **Sessions**: crash-free and error-free rates per release | one per page load | new session after 30 min in the background | one per process |
|
|
49
|
+
| Navigation breadcrumbs | any router (History API) | add `useDebugExpoRouter` or `trackReactNavigation` | |
|
|
50
|
+
| Clicks on buttons and links, by their label | ✓ | | |
|
|
51
|
+
| `console.warn` / `console.error` as breadcrumbs | ✓ | ✓ | off by default |
|
|
52
|
+
| Foreground/background, online/offline | ✓ | foreground/background | |
|
|
53
|
+
| Device, OS, app version, network type on every event | ✓ | ✓ | ✓ |
|
|
54
|
+
|
|
55
|
+
Every error carries the last 30 breadcrumbs, so the dashboard shows what happened just
|
|
56
|
+
before it: the screen, the click, the request that failed.
|
|
57
|
+
|
|
58
|
+
**Privacy.** Query strings and fragments are dropped from every URL before anything is
|
|
59
|
+
recorded, and ids in paths become `:id` (`/v1/orders/8812?token=…` becomes `/v1/orders/:id`).
|
|
60
|
+
Request and response bodies, headers and what users type are never read. Use `beforeSend` to
|
|
61
|
+
scrub anything else.
|
|
62
|
+
|
|
63
|
+
**Noise control.** At most 60 errors a minute are sent (`maxErrorsPerMinute`), and one repeating
|
|
64
|
+
error gets at most a sixth of that. Breadcrumbs that repeat are collapsed into one. `sampleRate`
|
|
65
|
+
keeps a fraction of errors and events. Sessions and fatal crashes are never sampled out, so
|
|
66
|
+
crash-free rates stay accurate.
|
|
67
|
+
|
|
68
|
+
### Tuning it
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
deBug.init({
|
|
72
|
+
endpoint, ingestKey, app: { id: "your-app", version: "2.4.0" },
|
|
73
|
+
autoCapture: { console: false, clicks: false }, // or `false` to turn everything off
|
|
74
|
+
network: {
|
|
75
|
+
flows: [["/v2/auth/", "auth.login"], [/\/payments\//, "checkout.payment"]], // tag requests by URL
|
|
76
|
+
ignoreUrls: ["analytics.example.com"],
|
|
77
|
+
isFailure: (status) => status >= 500 || status === 429, // default: 500 and above
|
|
78
|
+
slowMs: 3000, // slower requests get a warning breadcrumb
|
|
79
|
+
},
|
|
80
|
+
sampleRate: 1,
|
|
81
|
+
maxErrorsPerMinute: 60,
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Requests to your ingest endpoint and to the Metro and Expo dev servers are never recorded.
|
|
86
|
+
|
|
87
|
+
## Expo
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
// src/debug.ts
|
|
91
|
+
import { initExpo } from "@debugkit/sdk/expo";
|
|
92
|
+
|
|
93
|
+
initExpo({
|
|
94
|
+
endpoint: "https://<your-ingest-url>",
|
|
95
|
+
ingestKey: "pk_live_yourapp",
|
|
96
|
+
app: { id: "your-app" }, // version and build are read from expo-application
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
// src/app/_layout.tsx
|
|
102
|
+
import "@/debug";
|
|
103
|
+
import { DebugErrorBoundary } from "@debugkit/sdk/expo";
|
|
104
|
+
import { useDebugExpoRouter } from "@debugkit/sdk/expo-router";
|
|
105
|
+
|
|
106
|
+
export default function RootLayout() {
|
|
107
|
+
useDebugExpoRouter(); // a breadcrumb for every screen change
|
|
108
|
+
return (
|
|
109
|
+
<DebugErrorBoundary flow="app.root" fallback={({ reset }) => <CrashScreen onRetry={reset} />}>
|
|
110
|
+
<Stack />
|
|
111
|
+
</DebugErrorBoundary>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
In Expo Go, the app version reported is Expo Go's own. Development and production builds
|
|
117
|
+
report yours.
|
|
118
|
+
|
|
119
|
+
## Bare React Native
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import DeviceInfo from "react-native-device-info";
|
|
123
|
+
import { initReactNative, DebugErrorBoundary, trackReactNavigation } from "@debugkit/sdk/react-native";
|
|
124
|
+
|
|
125
|
+
initReactNative({ endpoint, ingestKey, app: { id: "your-app" }, deviceInfo: DeviceInfo });
|
|
126
|
+
|
|
127
|
+
const nav = useNavigationContainerRef();
|
|
128
|
+
<NavigationContainer ref={nav} {...trackReactNavigation(nav)}>…</NavigationContainer>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Without `react-native-device-info` you still get the OS, OS version and JS engine. On Android
|
|
132
|
+
you also get the model and brand.
|
|
133
|
+
|
|
134
|
+
React Native also gets:
|
|
135
|
+
|
|
136
|
+
- **Crashes** through React Native's global error handler.
|
|
137
|
+
- **Unhandled promise rejections** (Hermes). In production only by default, so the yellow-box
|
|
138
|
+
warning still works in development. Pass `captureUnhandledRejections: true` to capture them
|
|
139
|
+
in development too.
|
|
140
|
+
- **A flush when the app goes to the background.** Pass AsyncStorage as `storage` to keep
|
|
141
|
+
unsent events across restarts.
|
|
142
|
+
|
|
143
|
+
## React (web)
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
import { deBug, DebugErrorBoundary } from "@debugkit/sdk/react";
|
|
147
|
+
|
|
148
|
+
deBug.init({ endpoint, ingestKey, app: { id: "your-web-app", version: "1.4.0" } });
|
|
149
|
+
|
|
150
|
+
createRoot(el).render(
|
|
151
|
+
<DebugErrorBoundary fallback={<p>Something went wrong.</p>}>
|
|
152
|
+
<App />
|
|
153
|
+
</DebugErrorBoundary>,
|
|
154
|
+
);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Uncaught errors and unhandled rejections are captured from `window`. The OS, browser, screen
|
|
158
|
+
and network type are detected automatically.
|
|
159
|
+
|
|
160
|
+
## TanStack Start / Router
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
// src/router.tsx: the browser side
|
|
164
|
+
import { deBug, trackTanStackRouter } from "@debugkit/sdk/react";
|
|
165
|
+
|
|
166
|
+
if (typeof window !== "undefined") deBug.init({ endpoint, ingestKey, app: { id: "web" } });
|
|
167
|
+
|
|
168
|
+
export function getRouter() {
|
|
169
|
+
const router = createRouter({ routeTree });
|
|
170
|
+
if (typeof window !== "undefined") trackTanStackRouter(router);
|
|
171
|
+
return router;
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
// src/server/debug.ts: server only
|
|
177
|
+
import { deBug, initNode } from "@debugkit/sdk/node";
|
|
178
|
+
|
|
179
|
+
export const serverDebug = () =>
|
|
180
|
+
deBug.initialized ? deBug : initNode({ endpoint, ingestKey, app: { id: "web", environment: "server" } });
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
// src/server/fns.ts
|
|
185
|
+
import { createServerFn } from "@tanstack/react-start";
|
|
186
|
+
import { withDebug } from "@debugkit/sdk/node";
|
|
187
|
+
import { serverDebug } from "./debug";
|
|
188
|
+
|
|
189
|
+
export const charge = createServerFn({ method: "POST" }).handler(
|
|
190
|
+
withDebug("checkout.payment", async ({ data }) => {
|
|
191
|
+
serverDebug(); // initialise lazily, inside the handler, so none of this reaches the client bundle
|
|
192
|
+
/* … */
|
|
193
|
+
}),
|
|
194
|
+
);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
When the browser calls a server function that is wrapped in `withDebug`, record only the
|
|
198
|
+
outcome there, so the error isn't counted twice:
|
|
199
|
+
`deBug.track("checkout.payment", "charge", () => charge(), { captureError: false })`.
|
|
200
|
+
|
|
201
|
+
## Node
|
|
202
|
+
|
|
203
|
+
`initNode()` captures uncaught exceptions and unhandled rejections. It flushes events and then
|
|
204
|
+
exits the way Node would. Pass `captureProcessErrors: false` to manage those hooks yourself,
|
|
205
|
+
for example inside a dev server.
|
|
206
|
+
|
|
207
|
+
On edge runtimes such as Cloudflare Workers, timers aren't allowed at startup. Use the base
|
|
208
|
+
entry with `flushIntervalMs: 0`, and `await deBug.flush()` before each response returns.
|
|
209
|
+
|
|
210
|
+
## The API
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
deBug.error(err, { flow: "checkout.payment", step: "create_intent" });
|
|
214
|
+
deBug.event("signup_abandoned", { flow: "onboarding.signup", outcome: "abandoned" });
|
|
215
|
+
await deBug.track("auth.login", "submit_otp", () => api.verifyOtp(code)); // success or failure, plus the error
|
|
216
|
+
deBug.breadcrumb("navigation", "Login → OtpScreen");
|
|
217
|
+
deBug.setUser("user_123");
|
|
218
|
+
deBug.setContext({ app: { environment: "staging" } });
|
|
219
|
+
await deBug.flush();
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
`flow` names the part of your app, in dotted lowercase like `auth.login`. It's the main axis
|
|
223
|
+
of the dashboard, so keep it consistent.
|
|
224
|
+
|
|
225
|
+
To scrub personal data before anything leaves the device, use `beforeSend`:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
deBug.init({ …, beforeSend: (e) => (e.type === "event" && e.name === "secret" ? null : e) });
|
|
229
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { deBug, __publicField } from './chunk-5C7KJJNO.js';
|
|
2
|
+
import { Component, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
var DebugErrorBoundary = class extends Component {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
__publicField(this, "state", { error: null });
|
|
8
|
+
__publicField(this, "reset", () => this.setState({ error: null }));
|
|
9
|
+
}
|
|
10
|
+
static getDerivedStateFromError(error) {
|
|
11
|
+
return { error };
|
|
12
|
+
}
|
|
13
|
+
componentDidCatch(error, info) {
|
|
14
|
+
var _a, _b, _c;
|
|
15
|
+
const client = (_a = this.props.client) != null ? _a : deBug;
|
|
16
|
+
if (info.componentStack) {
|
|
17
|
+
client.breadcrumb("ui", "React render error", { componentStack: info.componentStack.trim().slice(0, 1500) }, "error");
|
|
18
|
+
}
|
|
19
|
+
client.error(error, { flow: this.props.flow, handled: false, tags: { source: "error-boundary" } });
|
|
20
|
+
void client.flush();
|
|
21
|
+
(_c = (_b = this.props).onError) == null ? void 0 : _c.call(_b, error, info);
|
|
22
|
+
}
|
|
23
|
+
render() {
|
|
24
|
+
const { error } = this.state;
|
|
25
|
+
if (!error) return this.props.children;
|
|
26
|
+
const { fallback } = this.props;
|
|
27
|
+
return typeof fallback === "function" ? fallback({ error, reset: this.reset }) : fallback != null ? fallback : null;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
function useDebugNavigation(path, client = deBug) {
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (path) client.navigated(path);
|
|
33
|
+
}, [path, client]);
|
|
34
|
+
}
|
|
35
|
+
function trackTanStackRouter(router, client = deBug) {
|
|
36
|
+
return router.subscribe("onResolved", ({ toLocation }) => client.navigated(toLocation.pathname));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { DebugErrorBoundary, trackTanStackRouter, useDebugNavigation };
|
|
40
|
+
//# sourceMappingURL=chunk-3WRI3DI2.js.map
|
|
41
|
+
//# sourceMappingURL=chunk-3WRI3DI2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx"],"names":[],"mappings":";;;AAqBO,IAAM,kBAAA,GAAN,cAAiC,SAAA,CAA0C;AAAA,EAA3E,WAAA,GAAA;AAAA,IAAA,KAAA,CAAA,GAAA,SAAA,CAAA;AACL,IAAA,aAAA,CAAA,IAAA,EAAA,OAAA,EAAe,EAAE,OAAO,IAAA,EAAK,CAAA;AAgB7B,IAAA,aAAA,CAAA,IAAA,EAAA,OAAA,EAAQ,MAAM,IAAA,CAAK,QAAA,CAAS,EAAE,KAAA,EAAO,MAAM,CAAA,CAAA;AAAA,EAAA;AAAA,EAd3C,OAAO,yBAAyB,KAAA,EAAqB;AACnD,IAAA,OAAO,EAAE,KAAA,EAAM;AAAA,EACjB;AAAA,EAEA,iBAAA,CAAkB,OAAc,IAAA,EAAiB;AA5BnD,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AA6BI,IAAA,MAAM,MAAA,GAAA,CAAS,EAAA,GAAA,IAAA,CAAK,KAAA,CAAM,MAAA,KAAX,IAAA,GAAA,EAAA,GAAqB,KAAA;AACpC,IAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,MAAA,MAAA,CAAO,UAAA,CAAW,IAAA,EAAM,oBAAA,EAAsB,EAAE,gBAAgB,IAAA,CAAK,cAAA,CAAe,IAAA,EAAK,CAAE,KAAA,CAAM,CAAA,EAAG,IAAI,CAAA,IAAK,OAAO,CAAA;AAAA,IACtH;AACA,IAAA,MAAA,CAAO,KAAA,CAAM,KAAA,EAAO,EAAE,IAAA,EAAM,KAAK,KAAA,CAAM,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,IAAA,EAAM,EAAE,MAAA,EAAQ,gBAAA,IAAoB,CAAA;AACjG,IAAA,KAAK,OAAO,KAAA,EAAM;AAClB,IAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,KAAA,EAAM,OAAA,KAAX,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAAqB,KAAA,EAAO,IAAA,CAAA;AAAA,EAC9B;AAAA,EAIA,MAAA,GAAS;AACP,IAAA,MAAM,EAAE,KAAA,EAAM,GAAI,IAAA,CAAK,KAAA;AACvB,IAAA,IAAI,CAAC,KAAA,EAAO,OAAO,IAAA,CAAK,KAAA,CAAM,QAAA;AAC9B,IAAA,MAAM,EAAE,QAAA,EAAS,GAAI,IAAA,CAAK,KAAA;AAC1B,IAAA,OAAO,OAAO,QAAA,KAAa,UAAA,GAAa,QAAA,CAAS,EAAE,KAAA,EAAO,KAAA,EAAO,IAAA,CAAK,KAAA,EAAO,CAAA,GAAK,QAAA,IAAA,IAAA,GAAA,QAAA,GAAY,IAAA;AAAA,EAChG;AACF;AAGO,SAAS,kBAAA,CAAmB,IAAA,EAA0B,MAAA,GAAsB,KAAA,EAAO;AACxF,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,IAAA,EAAM,MAAA,CAAO,SAAA,CAAU,IAAI,CAAA;AAAA,EACjC,CAAA,EAAG,CAAC,IAAA,EAAM,MAAM,CAAC,CAAA;AACnB;AAYO,SAAS,mBAAA,CAAoB,MAAA,EAAoB,MAAA,GAAsB,KAAA,EAAO;AACnF,EAAA,OAAO,MAAA,CAAO,SAAA,CAAU,YAAA,EAAc,CAAC,EAAE,UAAA,EAAW,KAAM,MAAA,CAAO,SAAA,CAAU,UAAA,CAAW,QAAQ,CAAC,CAAA;AACjG","file":"chunk-3WRI3DI2.js","sourcesContent":["import { Component, useEffect, type ErrorInfo, type ReactNode } from \"react\";\nimport { deBug, type DebugClient } from \"./index\";\n\nexport interface DebugErrorBoundaryProps {\n children?: ReactNode;\n /** What to render after a crash. A function gets the error and a reset callback. */\n fallback?: ReactNode | ((props: { error: Error; reset: () => void }) => ReactNode);\n /** Part of the app this boundary protects, e.g. \"checkout\". */\n flow?: string;\n client?: DebugClient;\n onError?: (error: Error, info: ErrorInfo) => void;\n}\n\ninterface State {\n error: Error | null;\n}\n\n/**\n * Catches render errors that React would otherwise swallow (or turn into a white/red screen)\n * and reports them. Works in React DOM and React Native.\n */\nexport class DebugErrorBoundary extends Component<DebugErrorBoundaryProps, State> {\n state: State = { error: null };\n\n static getDerivedStateFromError(error: Error): State {\n return { error };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n const client = this.props.client ?? deBug;\n if (info.componentStack) {\n client.breadcrumb(\"ui\", \"React render error\", { componentStack: info.componentStack.trim().slice(0, 1500) }, \"error\");\n }\n client.error(error, { flow: this.props.flow, handled: false, tags: { source: \"error-boundary\" } });\n void client.flush();\n this.props.onError?.(error, info);\n }\n\n reset = () => this.setState({ error: null });\n\n render() {\n const { error } = this.state;\n if (!error) return this.props.children;\n const { fallback } = this.props;\n return typeof fallback === \"function\" ? fallback({ error, reset: this.reset }) : (fallback ?? null);\n }\n}\n\n/** Adds a navigation breadcrumb whenever `path` changes. Works with any router. */\nexport function useDebugNavigation(path: string | undefined, client: DebugClient = deBug) {\n useEffect(() => {\n if (path) client.navigated(path);\n }, [path, client]);\n}\n\n/** Minimal shape of a TanStack Router instance (avoids a hard dependency). */\ninterface RouterLike {\n subscribe(event: \"onResolved\", fn: (e: { toLocation: { pathname: string; href?: string } }) => void): () => void;\n}\n\n/**\n * Records a breadcrumb for every navigation in a TanStack Router app:\n * const router = createRouter({ routeTree }); trackTanStackRouter(router);\n * Returns an unsubscribe function.\n */\nexport function trackTanStackRouter(router: RouterLike, client: DebugClient = deBug) {\n return router.subscribe(\"onResolved\", ({ toLocation }) => client.navigated(toLocation.pathname));\n}\n\nexport { deBug } from \"./index\";\n"]}
|