@hawk.so/nextjs 1.0.0 → 1.1.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 +161 -115
- package/dist/client.js +163 -0
- package/dist/{index.cjs → config.cjs} +82 -45
- package/dist/{index.d.cts → config.d.cts} +1 -1
- package/dist/config.d.ts +75 -0
- package/dist/{index.js → config.js} +72 -35
- package/dist/edge.js +435 -0
- package/dist/index.d.ts +280 -40
- package/dist/react.cjs +199 -0
- package/dist/react.js +179 -0
- package/dist/server.cjs +500 -0
- package/dist/server.js +452 -0
- package/package.json +38 -13
package/README.md
CHANGED
|
@@ -1,191 +1,227 @@
|
|
|
1
1
|
# Hawk Next.js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Error tracking for Next.js apps with [Hawk](https://hawk.so). Catches errors in the browser, on the server and in the Edge runtime, and sends releases with source maps, so errors from the browser show the original source code instead of minified chunks.
|
|
4
4
|
|
|
5
|
+
- 🐞 Errors from the browser, Node.js and Edge runtimes with one import
|
|
6
|
+
- 🧭 Errors of server components, route handlers, server actions and middleware with the route data
|
|
7
|
+
- 🧱 Ready error pages and error boundary for React
|
|
8
|
+
- 🙅 `redirect()`, `notFound()` and other Next.js control flow errors are skipped
|
|
9
|
+
- ⏱️ Events are delivered before serverless functions are frozen
|
|
5
10
|
- 🌟 Client source maps sending
|
|
6
11
|
- ⚡ Turbopack and webpack support
|
|
7
12
|
- 🏷️ Automatic release id from CI, git or build time
|
|
8
13
|
- 💬 Recent commits sending
|
|
9
14
|
- 🙈 Source maps removal after sending, so the source code is not published
|
|
10
|
-
- 🧩 One-line setup in `next.config`
|
|
11
15
|
- 🛡️ Build never fails because of sending errors
|
|
12
16
|
|
|
13
|
-
The package only sends releases, it doesn't catch errors. Use Hawk catchers for that, see [Error tracking](#error-tracking).
|
|
14
|
-
|
|
15
17
|
## Install
|
|
16
18
|
|
|
17
19
|
```shell
|
|
18
20
|
yarn add @hawk.so/nextjs
|
|
19
21
|
```
|
|
20
22
|
|
|
21
|
-
Install it as a regular dependency, not with `-D`: `next start` loads `next.config` too, and fails with `Cannot find module '@hawk.so/nextjs'` when production installs skip dev dependencies.
|
|
23
|
+
Install it as a regular dependency, not with `-D`: `next start` loads `next.config` too, and fails with `Cannot find module '@hawk.so/nextjs/config'` when production installs skip dev dependencies.
|
|
22
24
|
|
|
23
25
|
## Connect
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
Put your project's Integration Token to the `NEXT_PUBLIC_HAWK_TOKEN` environment variable of the build, e.g. in `.env`:
|
|
28
|
+
|
|
29
|
+
```shell
|
|
30
|
+
NEXT_PUBLIC_HAWK_TOKEN=eyJpbnRlZ3JhdGlvbklk...
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The plugin and the catchers read the same variable. The browser catcher has the token in the client code anyway, and Next.js inlines `NEXT_PUBLIC_` variables during the build, so the running app needs no Hawk variables.
|
|
34
|
+
|
|
35
|
+
Then add four files.
|
|
36
|
+
|
|
37
|
+
**1. `next.config.ts`** sends the release with source maps on `next build`:
|
|
26
38
|
|
|
27
39
|
```ts
|
|
28
|
-
// next.config.ts
|
|
29
40
|
import type { NextConfig } from 'next';
|
|
30
|
-
import { withHawkConfig } from '@hawk.so/nextjs';
|
|
41
|
+
import { withHawkConfig } from '@hawk.so/nextjs/config';
|
|
31
42
|
|
|
32
43
|
const nextConfig: NextConfig = {
|
|
33
44
|
// your config
|
|
34
45
|
};
|
|
35
46
|
|
|
36
|
-
export default withHawkConfig(nextConfig
|
|
37
|
-
token: process.env.HAWK_TOKEN, // Your project's Integration Token
|
|
38
|
-
});
|
|
47
|
+
export default withHawkConfig(nextConfig);
|
|
39
48
|
```
|
|
40
49
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
On `next build` the plugin:
|
|
50
|
+
**2. `instrumentation-client.ts`** in the project root (or in `src`) catches errors in the browser. Next.js runs it before the app starts:
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
3. after compilation sends recent commits and client source maps to Hawk
|
|
48
|
-
4. removes client source maps and `sourceMappingURL` comments from `.next/static`, so the source code is not published
|
|
52
|
+
```ts
|
|
53
|
+
import * as Hawk from '@hawk.so/nextjs';
|
|
49
54
|
|
|
50
|
-
|
|
55
|
+
Hawk.init();
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
export const onRouterTransitionStart = Hawk.onRouterTransitionStart;
|
|
58
|
+
```
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
| -- | -- | -- | -- |
|
|
56
|
-
| `token` | string | optional | Your project's Integration Token. Default is the `HAWK_TOKEN` environment variable. Without a token the release is not sent |
|
|
57
|
-
| `release` | string/number | optional | Unique identifier of the release. See [Release id](#release-id) |
|
|
58
|
-
| `removeSourceMaps` | boolean | optional | Shows if the plugin should remove client source maps after sending. Default is `true` |
|
|
59
|
-
| `commits` | boolean/object | optional | Sends recent commits with the release. Default is `true`. Pass `false` to disable or `{ repo, number }` to set the path to the git repository (default is the project directory) and the number of commits (default is `5`) |
|
|
60
|
+
**3. `instrumentation.ts`** catches errors on the server, in Node.js and Edge runtimes:
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
```ts
|
|
63
|
+
import * as Hawk from '@hawk.so/nextjs';
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
export function register() {
|
|
66
|
+
Hawk.init();
|
|
67
|
+
}
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
3. commit SHA from the CI environment: GitHub Actions (`GITHUB_SHA`), GitLab CI (`CI_COMMIT_SHA`), Vercel (`VERCEL_GIT_COMMIT_SHA`), Netlify, Cloudflare Pages, CircleCI and [others](./src/release.ts)
|
|
68
|
-
4. current commit: `git rev-parse HEAD`
|
|
69
|
-
5. build start time, e.g. `2026-10-27T18:30:00.000Z`
|
|
69
|
+
export const onRequestError = Hawk.onRequestError;
|
|
70
|
+
```
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
**4. `app/global-error.tsx`** catches errors of the root layout and shows a minimal error page:
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
```tsx
|
|
75
|
+
'use client';
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
ARG HAWK_RELEASE
|
|
77
|
-
RUN yarn build
|
|
77
|
+
export { HawkGlobalError as default } from '@hawk.so/nextjs';
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
docker build --build-arg HAWK_RELEASE=$(git rev-parse HEAD) .
|
|
82
|
-
```
|
|
80
|
+
Next.js only logs errors shown by error pages, so send them from your `error.tsx` files with `useHawkError`:
|
|
83
81
|
|
|
84
|
-
|
|
82
|
+
```tsx
|
|
83
|
+
'use client';
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
export default withHawkConfig(nextConfig, {
|
|
88
|
-
release: process.env.npm_package_version,
|
|
89
|
-
});
|
|
90
|
-
```
|
|
85
|
+
import { useHawkError } from '@hawk.so/nextjs';
|
|
91
86
|
|
|
92
|
-
|
|
87
|
+
export default function ErrorPage({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
|
|
88
|
+
useHawkError(error);
|
|
93
89
|
|
|
94
|
-
|
|
90
|
+
return <button type="button" onClick={reset}>Try again</button>;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
95
93
|
|
|
96
|
-
|
|
94
|
+
See the [example](./example) app with all of them.
|
|
97
95
|
|
|
98
|
-
|
|
99
|
-
- [@hawk.so/nodejs](https://github.com/codex-team/hawk.nodejs) on the server. Hawk doesn't apply source maps to server errors, so their stack traces point to the compiled files in `.next/server`.
|
|
96
|
+
## What is caught
|
|
100
97
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
98
|
+
| Error | Caught by |
|
|
99
|
+
| -- | -- |
|
|
100
|
+
| Browser: event handlers, effects, timers, unhandled rejections | `init()` in `instrumentation-client.ts` |
|
|
101
|
+
| Errors shown by `error.tsx` | `useHawkError(error)` |
|
|
102
|
+
| Errors of the root layout | `HawkGlobalError` in `global-error.tsx` |
|
|
103
|
+
| Errors of a component tree | `<HawkErrorBoundary>` |
|
|
104
|
+
| Server components, route handlers, server actions, middleware | `onRequestError` in `instrumentation.ts` |
|
|
105
|
+
| Uncaught exceptions and unhandled rejections of the Node.js server | `init()` in `instrumentation.ts` |
|
|
106
|
+
| Errors you catch yourself | `Hawk.send(error)` |
|
|
104
107
|
|
|
105
|
-
|
|
108
|
+
Next.js uses errors for `redirect()`, `notFound()`, `forbidden()` and dynamic rendering bailouts. They are part of the normal flow and are not sent.
|
|
106
109
|
|
|
107
|
-
|
|
110
|
+
Server errors reach the error pages in the browser with a `digest` and a generic message in production. They are already sent from the server, so `useHawkError` and the boundaries skip them. The server event has the same digest in `addons.nextjs.digest`, so you can find it by the digest shown on the page.
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
## API
|
|
110
113
|
|
|
111
|
-
|
|
112
|
-
// instrumentation-client.ts
|
|
113
|
-
import HawkCatcher from '@hawk.so/browser';
|
|
114
|
+
All functions are imported from `@hawk.so/nextjs` and work in the browser, on the server and in the Edge runtime: Next.js picks the build for each runtime.
|
|
114
115
|
|
|
115
|
-
|
|
116
|
+
| name | description |
|
|
117
|
+
| -- | -- |
|
|
118
|
+
| `init(options?)` | Creates the catcher. Call it in `instrumentation-client.ts` and in `register()` of `instrumentation.ts`. See [options](#init-options) |
|
|
119
|
+
| `send(error, context?, user?)` | Sends an error or a message. `user` is used on the server only, in the browser use `setUser()` |
|
|
120
|
+
| `setUser(user)`, `clearUser()` | Sets the user sent with every event, in the browser only |
|
|
121
|
+
| `setContext(context)` | Sets data sent with every event. On the server it is shared by all requests of the process |
|
|
122
|
+
| `breadcrumbs.add(breadcrumb)`, `.get()`, `.clear()` | Actions that happened before the error, in the browser only. Clicks, requests and navigations are added automatically |
|
|
123
|
+
| `flush(timeout?)` | Waits for the events being sent, 2 seconds at most by default. Resolves with `false` on timeout |
|
|
124
|
+
| `onRequestError` | Export it from `instrumentation.ts` to send errors of server components, route handlers, server actions and middleware |
|
|
125
|
+
| `onRouterTransitionStart` | Export it from `instrumentation-client.ts` to add a breadcrumb for every navigation |
|
|
126
|
+
| `useHawkError(error)` | Sends the error passed to `error.tsx` |
|
|
127
|
+
| `HawkGlobalError` | Ready `global-error.tsx` |
|
|
128
|
+
| `HawkErrorBoundary` | Error boundary that sends errors of its children. Props: `fallback` (element or `({ error, reset }) => element`) and `onError(error, info)` |
|
|
116
129
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
});
|
|
122
|
-
}
|
|
130
|
+
```tsx
|
|
131
|
+
<HawkErrorBoundary fallback={({ reset }) => <button onClick={reset}>Reload the widget</button>}>
|
|
132
|
+
<Widget />
|
|
133
|
+
</HawkErrorBoundary>
|
|
123
134
|
```
|
|
124
135
|
|
|
125
|
-
|
|
136
|
+
### Init options
|
|
126
137
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
138
|
+
| name | type | description |
|
|
139
|
+
| -- | -- | -- |
|
|
140
|
+
| `token` | string | Integration Token. Default is the `NEXT_PUBLIC_HAWK_TOKEN` environment variable, on the server `HAWK_TOKEN` is checked first. Without a token nothing is sent |
|
|
141
|
+
| `release` | string | Release id. Default is `process.env.HAWK_RELEASE` set by the plugin, see [Release id](#release-id) |
|
|
142
|
+
| `context` | object | Data sent with every event |
|
|
143
|
+
| `beforeSend` | function | Filters or modifies events. Return the event to send it or `false` to drop it |
|
|
144
|
+
| `disableGlobalErrorsHandling` | boolean | Disables catching of uncaught errors and unhandled rejections |
|
|
145
|
+
| `collectorEndpoint` | string | Collector endpoint for self-hosted Hawk: WebSocket URL in the browser, HTTP URL on the server |
|
|
146
|
+
| `user` | object | Current user, in the browser only |
|
|
147
|
+
| `breadcrumbs` | false/object | Breadcrumbs options of [@hawk.so/browser](https://github.com/codex-team/hawk.javascript/tree/master/packages/browser), in the browser only |
|
|
148
|
+
| `consoleTracking` | boolean | Sends recent console output with the events, in the browser only. Default is `true` |
|
|
149
|
+
| `issues` | false/object | Web Vitals and long tasks detection of [@hawk.so/browser](https://github.com/codex-team/hawk.javascript/tree/master/packages/browser), in the browser only |
|
|
130
150
|
|
|
131
|
-
|
|
151
|
+
Pass different options to the browser and the server by calling `init()` with them in each file.
|
|
132
152
|
|
|
133
|
-
|
|
134
|
-
useEffect(() => {
|
|
135
|
-
// Server errors have a digest and are sent by the Node.js catcher
|
|
136
|
-
if (error.digest === undefined) {
|
|
137
|
-
reportError(error);
|
|
138
|
-
}
|
|
139
|
-
}, [error]);
|
|
153
|
+
### On the server
|
|
140
154
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
155
|
+
- The server handles requests of many users at once, so there is no current user: `setUser()` is ignored. Pass the user with the event instead: `Hawk.send(error, { orderId }, { id: user.id })`.
|
|
156
|
+
- `onRequestError` waits for sending and passes it to `waitUntil` where the platform supports it, e.g. on Vercel. If you call `send()` right before a serverless function returns, wait for it with `await Hawk.flush()`.
|
|
157
|
+
- Events have Next.js data in `addons.nextjs`: `runtime`, `routerKind`, `routePath`, `routeType`, `method`, `path`, `digest` and the `host`, `referer` and `user-agent` headers. Cookies, authorization and IP addresses are not sent.
|
|
158
|
+
- The Edge runtime has no process events, so only errors passed to `onRequestError` and `send()` are sent. `next start` passes errors of Edge routes to `onRequestError` of the Node.js runtime.
|
|
159
|
+
- Hawk doesn't apply source maps to server errors yet, so their stack traces point to the compiled files in `.next/server`.
|
|
144
160
|
|
|
145
|
-
|
|
161
|
+
## Plugin options
|
|
146
162
|
|
|
147
|
-
|
|
163
|
+
Pass them as the second argument of `withHawkConfig`:
|
|
148
164
|
|
|
149
165
|
```ts
|
|
150
|
-
|
|
151
|
-
|
|
166
|
+
export default withHawkConfig(nextConfig, {
|
|
167
|
+
commits: false,
|
|
168
|
+
});
|
|
169
|
+
```
|
|
152
170
|
|
|
153
|
-
|
|
154
|
-
|
|
171
|
+
| name | type | required | description |
|
|
172
|
+
| -- | -- | -- | -- |
|
|
173
|
+
| `token` | string | optional | Your project's Integration Token. Default is the `HAWK_TOKEN` or `NEXT_PUBLIC_HAWK_TOKEN` environment variable. Without a token the release is not sent |
|
|
174
|
+
| `release` | string/number | optional | Unique identifier of the release. See [Release id](#release-id) |
|
|
175
|
+
| `removeSourceMaps` | boolean | optional | Shows if the plugin should remove client source maps after sending. Default is `true` |
|
|
176
|
+
| `commits` | boolean/object | optional | Sends recent commits with the release. Default is `true`. Pass `false` to disable or `{ repo, number }` to set the path to the git repository (default is the project directory) and the number of commits (default is `5`) |
|
|
155
177
|
|
|
156
|
-
|
|
157
|
-
const { default: HawkCatcher } = await import('@hawk.so/nodejs');
|
|
178
|
+
Config functions are supported as well: `withHawkConfig((phase, { defaultConfig }) => ({ ... }), options)`.
|
|
158
179
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
180
|
+
On `next build` the plugin:
|
|
181
|
+
|
|
182
|
+
1. resolves the release id and exposes it to your code as `process.env.HAWK_RELEASE`
|
|
183
|
+
2. enables client source maps (`productionBrowserSourceMaps`) unless you set it yourself
|
|
184
|
+
3. after compilation sends recent commits and client source maps to Hawk
|
|
185
|
+
4. removes client source maps and `sourceMappingURL` comments from `.next/static`, so the source code is not published
|
|
165
186
|
|
|
166
|
-
|
|
167
|
-
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
168
|
-
const { default: HawkCatcher } = await import('@hawk.so/nodejs');
|
|
187
|
+
`next dev` sends nothing.
|
|
169
188
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
189
|
+
### Release id
|
|
190
|
+
|
|
191
|
+
Release id links error events to the source maps and commits sent from the build. The plugin picks it once per build, in this order:
|
|
192
|
+
|
|
193
|
+
1. `release` option
|
|
194
|
+
2. `HAWK_RELEASE` environment variable
|
|
195
|
+
3. commit SHA from the CI environment: GitHub Actions (`GITHUB_SHA`), GitLab CI (`CI_COMMIT_SHA`), Vercel (`VERCEL_GIT_COMMIT_SHA`), Netlify, Cloudflare Pages, CircleCI and [others](./src/config/release.ts)
|
|
196
|
+
4. current commit: `git rev-parse HEAD`
|
|
197
|
+
5. build start time, e.g. `2026-10-27T18:30:00.000Z`
|
|
198
|
+
|
|
199
|
+
Whatever is picked, the source maps and your code get the same value: the plugin inlines it into the build as `process.env.HAWK_RELEASE`, and `init()` takes it from there. You don't need to set this variable when the app runs.
|
|
200
|
+
|
|
201
|
+
Production builds often run without git and CI variables, for example in a Docker build or a separate Kubernetes pod. Then the release is the build time. Source maps still work, but commits are not sent and the release name tells nothing about the deployed version. To get meaningful releases, pass the id from your pipeline with the `HAWK_RELEASE` environment variable:
|
|
202
|
+
|
|
203
|
+
```dockerfile
|
|
204
|
+
ARG HAWK_RELEASE
|
|
205
|
+
RUN yarn build
|
|
178
206
|
```
|
|
179
207
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
208
|
+
```shell
|
|
209
|
+
docker build --build-arg HAWK_RELEASE=$(git rev-parse HEAD) .
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Or set the `release` option, for example to the version from `package.json`, which package managers expose while running `yarn build` or `npm run build`:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
export default withHawkConfig(nextConfig, {
|
|
216
|
+
release: process.env.npm_package_version,
|
|
217
|
+
});
|
|
218
|
+
```
|
|
183
219
|
|
|
184
|
-
|
|
220
|
+
A version is unique only if you bump it for every deploy, otherwise errors of different builds get into the same release.
|
|
185
221
|
|
|
186
222
|
### TypeScript
|
|
187
223
|
|
|
188
|
-
`process.env.HAWK_RELEASE` is typed automatically when you import the
|
|
224
|
+
`process.env.HAWK_RELEASE` is typed automatically when you import the package. If your config is a JavaScript file and you don't import the package in TypeScript files, add the declaration to `tsconfig.json`:
|
|
189
225
|
|
|
190
226
|
```json
|
|
191
227
|
{
|
|
@@ -195,9 +231,19 @@ See the [example](./example) app with both catchers.
|
|
|
195
231
|
}
|
|
196
232
|
```
|
|
197
233
|
|
|
234
|
+
## Migration from Hawk catchers
|
|
235
|
+
|
|
236
|
+
If you connected `@hawk.so/browser` and `@hawk.so/nodejs` yourself:
|
|
237
|
+
|
|
238
|
+
1. Import `withHawkConfig` from `@hawk.so/nextjs/config` instead of `@hawk.so/nextjs`.
|
|
239
|
+
2. Replace the catchers in `instrumentation-client.ts` and `instrumentation.ts` with the files from [Connect](#connect).
|
|
240
|
+
3. Replace `reportError(error)` in `error.tsx` with `useHawkError(error)`.
|
|
241
|
+
4. Remove `@hawk.so/browser` and `@hawk.so/nodejs` from your dependencies, and `HAWK_TOKEN` if it has the same value as `NEXT_PUBLIC_HAWK_TOKEN`.
|
|
242
|
+
|
|
198
243
|
## Requirements
|
|
199
244
|
|
|
200
245
|
- Next.js 15.4.1 or newer: the plugin uses the `compiler.runAfterProductionCompile` build hook
|
|
246
|
+
- React 18.2 or newer
|
|
201
247
|
- Node.js 20.9 or newer
|
|
202
248
|
|
|
203
249
|
## Notes
|
|
@@ -213,7 +259,7 @@ See the [example](./example) app with both catchers.
|
|
|
213
259
|
yarn # install dependencies
|
|
214
260
|
yarn lint # ESLint
|
|
215
261
|
yarn test # unit tests
|
|
216
|
-
yarn e2e # build the example app and
|
|
262
|
+
yarn e2e # build and start the example app, check the release and the events received by a mock collector
|
|
217
263
|
```
|
|
218
264
|
|
|
219
|
-
`yarn e2e --debug-ids` checks indexed source maps produced with `turbopack.debugIds`, `yarn e2e --webpack` checks the webpack build.
|
|
265
|
+
`yarn e2e` opens the example in Chromium, install it once with `yarn playwright install chromium`. `yarn e2e --debug-ids` checks indexed source maps produced with `turbopack.debugIds`, `yarn e2e --webpack` checks the webpack build.
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// src/client/index.ts
|
|
2
|
+
import HawkCatcher from "@hawk.so/browser";
|
|
3
|
+
import { log } from "@hawk.so/core";
|
|
4
|
+
|
|
5
|
+
// src/common/carrier.ts
|
|
6
|
+
var CARRIER_KEY = /* @__PURE__ */ Symbol.for("hawk.nextjs");
|
|
7
|
+
function getCarrier() {
|
|
8
|
+
const store = globalThis;
|
|
9
|
+
store[CARRIER_KEY] ??= {};
|
|
10
|
+
return store[CARRIER_KEY];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/common/control-flow.ts
|
|
14
|
+
var CONTROL_FLOW_DIGEST_PREFIXES = [
|
|
15
|
+
"NEXT_REDIRECT",
|
|
16
|
+
"NEXT_HTTP_ERROR_FALLBACK;",
|
|
17
|
+
"NEXT_NOT_FOUND",
|
|
18
|
+
"BAILOUT_TO_CLIENT_SIDE_RENDERING",
|
|
19
|
+
"DYNAMIC_SERVER_USAGE",
|
|
20
|
+
"NEXT_PRERENDER_INTERRUPTED",
|
|
21
|
+
"HANGING_PROMISE_REJECTION"
|
|
22
|
+
];
|
|
23
|
+
var STATIC_GEN_BAILOUT_CODE = "NEXT_STATIC_GEN_BAILOUT";
|
|
24
|
+
var REACT_POSTPONE_TYPE = /* @__PURE__ */ Symbol.for("react.postpone");
|
|
25
|
+
var MAX_CAUSE_DEPTH = 5;
|
|
26
|
+
function isControlFlowDigest(value) {
|
|
27
|
+
return typeof value === "string" && CONTROL_FLOW_DIGEST_PREFIXES.some((prefix) => value.startsWith(prefix));
|
|
28
|
+
}
|
|
29
|
+
function isNextControlFlowError(error) {
|
|
30
|
+
let current = error;
|
|
31
|
+
for (let depth = 0; depth < MAX_CAUSE_DEPTH; depth++) {
|
|
32
|
+
if (typeof current !== "object" || current === null) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const value = current;
|
|
36
|
+
if (isControlFlowDigest(value.digest) || isControlFlowDigest(value.message) || value.code === STATIC_GEN_BAILOUT_CODE || value.$$typeof === REACT_POSTPONE_TYPE) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
current = value.cause;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
function isNextControlFlowTitle(title) {
|
|
44
|
+
return isControlFlowDigest(title);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/common/error.ts
|
|
48
|
+
function isErrorLike(value) {
|
|
49
|
+
return typeof value === "object" && value !== null && typeof value.message === "string" && typeof value.stack === "string";
|
|
50
|
+
}
|
|
51
|
+
function normalizeError(value) {
|
|
52
|
+
if (value instanceof Error || typeof value === "string") {
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
if (isErrorLike(value)) {
|
|
56
|
+
const error = new Error(value.message);
|
|
57
|
+
error.name = typeof value.name === "string" ? value.name : error.name;
|
|
58
|
+
error.stack = value.stack;
|
|
59
|
+
return error;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
return JSON.stringify(value) ?? String(value);
|
|
63
|
+
} catch (_error) {
|
|
64
|
+
return String(value);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/client/index.ts
|
|
69
|
+
import { HawkErrorBoundary, HawkGlobalError, useHawkError } from "./react.js";
|
|
70
|
+
var isUserWarningShown = false;
|
|
71
|
+
function getCatcher() {
|
|
72
|
+
return getCarrier().catcher;
|
|
73
|
+
}
|
|
74
|
+
function captureWithAddons(catcher, error, addons) {
|
|
75
|
+
const integrations = {
|
|
76
|
+
nextjs: {
|
|
77
|
+
runtime: "browser",
|
|
78
|
+
...addons
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
catcher.captureError(normalizeError(error), integrations);
|
|
82
|
+
}
|
|
83
|
+
function init(options = {}) {
|
|
84
|
+
const carrier = getCarrier();
|
|
85
|
+
if (carrier.catcher !== void 0) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const { beforeSend, ...settings } = options;
|
|
89
|
+
const token = options.token ?? process.env.NEXT_PUBLIC_HAWK_TOKEN;
|
|
90
|
+
if (token === void 0 || token === "") {
|
|
91
|
+
log("Integration Token is not provided, errors will not be sent. Set NEXT_PUBLIC_HAWK_TOKEN or pass `token` to init()", "warn");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const filter = (event) => {
|
|
95
|
+
if (isNextControlFlowTitle(event.title)) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return beforeSend === void 0 ? event : beforeSend(event);
|
|
99
|
+
};
|
|
100
|
+
const catcher = new HawkCatcher({
|
|
101
|
+
...settings,
|
|
102
|
+
token,
|
|
103
|
+
release: options.release ?? process.env.HAWK_RELEASE,
|
|
104
|
+
beforeSend: filter
|
|
105
|
+
});
|
|
106
|
+
carrier.catcher = catcher;
|
|
107
|
+
carrier.capture = (error, addons) => {
|
|
108
|
+
captureWithAddons(catcher, error, addons);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function send(error, context, user) {
|
|
112
|
+
if (user !== void 0 && !isUserWarningShown) {
|
|
113
|
+
isUserWarningShown = true;
|
|
114
|
+
log("The user passed to send() is ignored in the browser, use setUser() instead", "warn");
|
|
115
|
+
}
|
|
116
|
+
if (isNextControlFlowError(error)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
getCatcher()?.send(normalizeError(error), context);
|
|
120
|
+
}
|
|
121
|
+
function setUser(user) {
|
|
122
|
+
getCatcher()?.setUser(user);
|
|
123
|
+
}
|
|
124
|
+
function clearUser() {
|
|
125
|
+
getCatcher()?.clearUser();
|
|
126
|
+
}
|
|
127
|
+
function setContext(context) {
|
|
128
|
+
getCatcher()?.setContext(context);
|
|
129
|
+
}
|
|
130
|
+
var breadcrumbs = {
|
|
131
|
+
add: (breadcrumb, hint) => getCatcher()?.breadcrumbs.add(breadcrumb, hint),
|
|
132
|
+
get: () => getCatcher()?.breadcrumbs.get() ?? [],
|
|
133
|
+
clear: () => getCatcher()?.breadcrumbs.clear()
|
|
134
|
+
};
|
|
135
|
+
function flush(_timeout) {
|
|
136
|
+
return Promise.resolve(true);
|
|
137
|
+
}
|
|
138
|
+
function onRequestError(_error, _request, _context) {
|
|
139
|
+
return Promise.resolve();
|
|
140
|
+
}
|
|
141
|
+
function onRouterTransitionStart(url, navigationType) {
|
|
142
|
+
getCatcher()?.breadcrumbs.add({
|
|
143
|
+
type: "navigation",
|
|
144
|
+
category: "next.router",
|
|
145
|
+
message: url,
|
|
146
|
+
level: "info",
|
|
147
|
+
data: { navigationType }
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
export {
|
|
151
|
+
HawkErrorBoundary,
|
|
152
|
+
HawkGlobalError,
|
|
153
|
+
breadcrumbs,
|
|
154
|
+
clearUser,
|
|
155
|
+
flush,
|
|
156
|
+
init,
|
|
157
|
+
onRequestError,
|
|
158
|
+
onRouterTransitionStart,
|
|
159
|
+
send,
|
|
160
|
+
setContext,
|
|
161
|
+
setUser,
|
|
162
|
+
useHawkError
|
|
163
|
+
};
|