# Hawk JavaScript Browser Catcher

Error tracking for JavaScript/TypeScript browser applications.

Former it was named [@hawk.so/javascript](https://www.npmjs.com/package/@hawk.so/javascript).

## Features

- 🦅 Automatic error catching
- 💎 Manual error and logs sending
- 🙂 Attaching user information
- 📦 Attaching additional context
- 🛡️ Sensitive data filtering
- 🌟 Source maps consuming
- 💬 Console logs tracking
- 🧊 Performance issues detection (Long Tasks + Long Animation Frames)
- 📊 Web Vitals issues monitoring
- <img src="https://cdn.svglogos.dev/logos/vue.svg" width="16" height="16"> &nbsp;Vue support
- <img src="https://cdn.svglogos.dev/logos/react.svg" width="16" height="16">  &nbsp;React support


## Installation

We recommend adding Hawk script to page above others to prevent missing any errors.

### Install via NPM or Yarn

Install package

```shell
npm install @hawk.so/browser --save
```

```shell
yarn add @hawk.so/browser
```

Then import `@hawk.so/browser` module to your code.

```js
import HawkCatcher from '@hawk.so/browser';
```

### Load from CDN

Get a specific version bundle path from [@hawk.so/browser](https://www.jsdelivr.com/package/npm/@hawk.so/browser)
— open the page and copy the link. Do not use @latest, as your setup may break in case of a major API update.

Then require this script on your site.

```
<script src="..." async></script>
```

## Usage

### Get an Integration Token

First of all, you should register an account on
[hawk.so](https://hawk-tracker.ru/signup?utm_source=github&utm_medium=readme&utm_campaign=js_sdk&utm_content=signup_link).

Then create a Workspace and a Project in there. You'll get an Integration Token.

### Initialize Catcher

Create `HawkCatcher` class instance when script will be ready and pass your Integration Token:

```js
const hawk = new HawkCatcher({token: 'INTEGRATION_TOKEN'});

// or

const hawk = new HawkCatcher('INTEGRATION_TOKEN');
```

Alternately, add `onload="const hawk = new HawkCatcher({token: 'INTEGRATION_TOKEN'})"` attribute to the `<script>` tag.

```html
<script src="https://cdn.jsdelivr.net/npm/@hawk.so/browser@latest/dist/hawk.js"
        onload="const hawk = new HawkCatcher({token: 'INTEGRATION_TOKEN'})"></script>
```

Initialization settings:

| name                          | type                                                      | required     | description                                                                         |
|-------------------------------|-----------------------------------------------------------|--------------|-------------------------------------------------------------------------------------|
| `token`                       | string                                                    | **required** | Your project's Integration Token                                                    |
| `release`                     | string/number                                             | optional     | Unique identifier of the release. Used for source map consuming (see below)         |
| `user`                        | {id: string, name?: string, image?: string, url?: string} | optional     | Current authenticated user                                                          |
| `context`                     | object                                                    | optional     | Any data you want to pass with every message. Has limitation of length.             |
| `vue`                         | Vue constructor                                           | optional     | Pass Vue constructor to set up the [Vue integration](#integrate-to-vue-application) |
| `disableVueErrorHandler`      | boolean                                                   | optional     | Do not initialize Vue errors handling                                               |
| `consoleTracking`             | boolean                                                   | optional     | Initialize console logs tracking                                                    |
| `breadcrumbs`                 | false or BreadcrumbsOptions object                        | optional     | Configure breadcrumbs tracking (see below)                                          |
| `beforeSend`                  | function(event) => event \| false \| void                 | optional     | Filter data before sending. Return modified event, `false` to drop the event.       |
| `issues`                      | IssuesOptions object                                      | optional     | Issues config. See [Issues configuration](#issues-configuration). |

Other available [initial settings](types/hawk-initial-settings.d.ts) are described at the type definition.

## Manual sending

You can send errors or other messages to the Hawk manually, for example at your `catch` blocks or any debug conditions.

Use the `.send(message, context)` method for that. This method accepts the `message` of type `Error` or `string`
as the first parameter. The second parameter is optional, it allows passing any additional data with the event.
If you specify the `context` with the `HawkCatcher` constructor, it will be merged with the context passed to the `send`
method.

```js
// init Hawk Catcher instance
const hawk = new HawkCatcher({token: 'INTEGRATION_TOKEN'});

// somewhere in try-catch block or other custom place
hawk.send(new Error('Something went wrong'), {
  myOwnDebugInfo: '1234',
});
```

## User Management

You can dynamically manage user information after the catcher is initialized:

```js
const hawk = new HawkCatcher({token: 'INTEGRATION_TOKEN'});

// Set user information
hawk.setUser({
  id: 'user123',
  name: 'John Doe',
  url: '/users/123',
  image: 'https://example.com/avatar.jpg',
});

// Clear user (revert to generated user)
hawk.clearUser();
```

## Context Management

You can dynamically update context data that will be sent with all events:

```js
const hawk = new HawkCatcher({token: 'INTEGRATION_TOKEN'});

// Set context data
hawk.setContext({
  feature: 'user-dashboard',
  version: '2.1.0',
  environment: 'production',
});
```

## Breadcrumbs

Breadcrumbs track user interactions and events leading up to an error, providing context for debugging.

### Default Configuration

By default, breadcrumbs are enabled with tracking for fetch/XHR requests, navigation, and UI clicks:

```js
const hawk = new HawkCatcher({
  token: 'INTEGRATION_TOKEN'
  // breadcrumbs enabled by default
});
```

### Disabling Breadcrumbs

To disable breadcrumbs entirely:

```js
const hawk = new HawkCatcher({
  token: 'INTEGRATION_TOKEN',
  breadcrumbs: false
});
```

### Custom Configuration

Configure breadcrumbs tracking behavior:

```js
const hawk = new HawkCatcher({
  token: 'INTEGRATION_TOKEN',
  breadcrumbs: {
    maxBreadcrumbs: 20,         // Maximum breadcrumbs to store (default: 15)
    trackFetch: true,           // Track fetch/XHR requests (default: true)
    trackNavigation: true,      // Track navigation events (default: true)
    trackClicks: true,          // Track UI clicks (default: true)
    beforeBreadcrumb: (breadcrumb, hint) => {
      // Filter or modify breadcrumbs before storing
      if (breadcrumb.category === 'fetch' && breadcrumb.data?.url?.includes('/sensitive')) {
        return false; // Discard this breadcrumb
      }
      return breadcrumb;
    }
  }
});
```

### Breadcrumbs Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `maxBreadcrumbs` | `number` | `15` | Maximum number of breadcrumbs to store. When the limit is reached, oldest breadcrumbs are removed (FIFO). |
| `trackFetch` | `boolean` | `true` | Automatically track `fetch()` and `XMLHttpRequest` calls as breadcrumbs. Captures request URL, method, status code, and response time. |
| `trackNavigation` | `boolean` | `true` | Automatically track navigation events (History API: `pushState`, `replaceState`, `popstate`). Captures route changes. |
| `trackClicks` | `boolean` | `true` | Automatically track UI click events. Captures element selector, coordinates, and other click metadata. |
| `beforeBreadcrumb` | `function` | `undefined` | Hook called before each breadcrumb is stored. Receives `(breadcrumb, hint)`. Return modified breadcrumb to keep it, `false` to discard. |

### Manual Breadcrumbs

Add custom breadcrumbs manually:

```js
hawk.breadcrumbs.add({
  type: 'logic',
  category: 'auth',
  message: 'User logged in',
  level: 'info',
  data: { userId: '123' }
});
```

### Breadcrumb Methods

```js
// Add a breadcrumb
hawk.breadcrumbs.add(breadcrumb, hint);

// Get current breadcrumbs
const breadcrumbs = hawk.breadcrumbs.get();

// Clear all breadcrumbs
hawk.breadcrumbs.clear();
```

## Issues configuration

The `issues` option configures automatic performance and error tracking.

| detector | key | default threshold | what it reports |
|---|---|---|---|
| **Errors** | `issues.errors` | — | Global runtime errors (`window.onerror`, `unhandledrejection`) |
| **Web Vitals** | `issues.webVitals` | custom `reportPoorAbove` per metric | Core Web Vitals that pass Hawk report thresholds (`LCP`, `FCP`, `TTFB`, `INP`, `CLS`) |
| **Long Tasks** | `issues.longTasks` | `100 ms` | Tasks with identifiable container (`containerSrc`, `containerId`, or `containerName`) |
| **Long Animation Frames** | `issues.longAnimationFrames` | `300 ms` | Frames where at least one script attribution has `sourceURL`, `sourceFunctionName`, or `invoker` |

Default `issues` behavior (when omitted or set to `{}`): performance detectors are off, global error listeners are on.

```js
issues: {
  errors: true,
  webVitals: false,
  longTasks: false,
  longAnimationFrames: false
}
```

Pass `true` or an options object for any detector you want. If the browser does not support a specific Performance API (`longtask`, `long-animation-frame`), the corresponding detector is silently skipped.

Performance data is transmitted in the event **addons** (keys: `Long Task`, `Long Animation Frame`, `Web Vitals`).

> [!WARNING]
> To avoid overloading your event list, set a Grouping Pattern in Project Settings to merge all `Long Task: ...` / `Long Animation Frame: ...` issues.
>
> Example:
> ```regex
> Long Task.+
> Long Animation Frame.+
> ```

### Web Vitals

When `issues.webVitals` is enabled, Hawk subscribes to Core Web Vitals via the `web-vitals` library and sends one issue per metric name per page load when metric value exceeds the configured `reportPoorAbove` threshold.

Default `reportPoorAbove` values:

| metric | default `reportPoorAbove` |
|---|---|
| `CLS` | `0.35` |
| `INP` | `700` |
| `LCP` | `5000` |
| `FCP` | `4000` |
| `TTFB` | `2500` |

You can override them:

```js
const hawk = new HawkCatcher({
  token: 'INTEGRATION_TOKEN',
  issues: {
    webVitals: {
      reportPoorAbove: {
        CLS: 0.3,
        INP: 600,
        LCP: 4500,
        FCP: 3500,
        TTFB: 2200,
      }
    }
  }
});
```

`web-vitals` is included in the SDK dependencies — no extra installation required.

### Disabling

Disable **all** automatic issue tracking (errors, Web Vitals, Long Tasks, LoAF).
Manual sending via `hawk.send()` still works:

```js
const hawk = new HawkCatcher({
  token: 'INTEGRATION_TOKEN',
  issues: false
});
```

Disable only global errors handling:

```js
const hawk = new HawkCatcher({
  token: 'INTEGRATION_TOKEN',
  issues: {
    errors: false
  }
});
```

### Selective Configuration

Enable or disable individual detectors, optionally overriding thresholds (minimum `50 ms`):

```js
const hawk = new HawkCatcher({
  token: 'INTEGRATION_TOKEN',
  issues: {
    errors: true,
    webVitals: {
      reportPoorAbove: {
        INP: 600
      }
    },
    longTasks: {
      thresholdMs: 70
    },
    longAnimationFrames: {
      thresholdMs: 200
    }
  }
});
```

## Source maps consuming

If your bundle is minified, it is useful to pass source-map files to the Hawk. After that you will see beautiful
original source code lines in Hawk Garage instead of minified code.

To enable source map consuming you should do two things:

- Send the source map and the release identifier to the Hawk after you build a new version of the script. For example
  with the [Hawk Webpack Plugin](https://github.com/codex-team/hawk.webpack.plugin) or with cURL request.
- Pass the release identifier to the Hawk Catcher using `release` option.

## Testing and server responses

To make sure that Hawk is working right, call `test()` method from `HawkCatcher` class instance in browser's console.
`test()` method sends fake error to server. Then, open Hawk and find a test event at the Project's page.

## Sensitive data filtering

You can filter any data that you don't want to send to Hawk. Use the `beforeSend()` hook for that reason.

```js
window.hawk = new HawkCatcher({
  token: 'INTEGRATION TOKEN',
  beforeSend(event) {
    if (event.user && event.user.name) {
      delete event.user.name;
    }

    return event;
  }
})
```

## Dismiss error

You can use the `beforeSend()` hook to prevent sending a particular event. Return `false` for that.

## Usage with &nbsp; <img src="https://cdn.svglogos.dev/logos/vue.svg" width="22"> &nbsp;Vue.js

Vue apps have their own error handler, so if you want to catcher errors thrown inside Vue components, you should set up
a Vue integration.

Pass the Vue constructor with the initial settings:

```js
import Vue from 'vue';

const hawk = new HawkCatcher({
  token: 'INTEGRATION_TOKEN',
  vue: Vue // the Vue constructor you tweak
});
```

or pass it any moment after Hawk Catcher was instantiated:

```js
import Vue from 'vue';

const hawk = new HawkCatcher({
  token: 'INTEGRATION_TOKEN',
});

hawk.connectVue(Vue)
```

## Usage with &nbsp; <img src="https://cdn.svglogos.dev/logos/react.svg" width="22"> &nbsp;React

React is suppported out of the box. No additional setup required.

Create the Hawk Catcher instance in a `index.js` file of your project.

```js
import HawkCatcher from '@hawk.so/browser';

const hawk = new HawkCatcher({
  token: 'INTEGRATION_TOKEN'
});
```

## License

This project is licensed under the **GNU Affero General Public License v3.0 (AGPL-3.0)**.
See the [LICENSE](../../LICENSE) file for the full text.
