# API reference

> Every export, option, type and default.

Source: https://whiskerjs.com/docs/api/ (last updated 2026-10-26)

> **In plain words:**
>
> There are two calls: `load()` makes an agent and `agent.collect()` makes one result. Everything else is constants and types that describe the result. `collect()` never throws.

Everything below is exported from `whiskerjs` (the package’s only entry point, ES modules). Version 0.1.0, signalsVersion 9, idVersion 2.

## Functions

### `load(options?: LoadOptions): Promise<Agent>`

Creates an agent. Does no collection work and keeps no state between `collect()` calls. Never rejects: if `options.env` cannot be used, the default environment is used.

### `agent.collect(options?: CollectOptions): Promise<CollectResult>`

Runs one collection. Never throws and never rejects. Each call is independent.

### `bindingInput(result): string`

Returns the exact text the binding digest is computed over (see [Anti-replay binding](https://whiskerjs.com/docs/integration/#the-digest)). Takes a result with its `binding`; `binding.digest` is not part of the text. Pure: usable on a server.

## Options

### `LoadOptions`

| Option | Type | Default | Meaning |
| --- | --- | --- | --- |
| `env` | `Partial<Environment>` | the real browser globals | override what the library touches; mainly for tests |
| `onResult` | `(result: CollectResult) => void \| Promise<void>` | none (off) | called once per `collect()` with a deep copy of the result; errors are swallowed, not awaited. See [Integration](https://whiskerjs.com/docs/integration/#the-onresult-hook) |

### `Environment`

| Field | Type | Default |
| --- | --- | --- |
| `window` | `WindowLike` (any Window-like object) | `globalThis` |
| `now()` | `() => number` (milliseconds) | `window.performance.now()`, else `Date.now()` |
| `setTimeout(fn, ms)` | timer | `globalThis.setTimeout` |
| `clearTimeout(handle)` | timer | `globalThis.clearTimeout` |
| `subtle` | `DigestLike` or `undefined` | `window.crypto.subtle`, else `globalThis.crypto.subtle` |

Passing `subtle: undefined` explicitly simulates a browser without Web Crypto.

### `CollectOptions`

| Option | Type | Default | Meaning |
| --- | --- | --- | --- |
| `timeoutMs` | `number` | `5000` | overall budget; clamped to 0 to 60000; not a finite number means 5000 |
| `signal` | `AbortSignal` | none | aborting resolves every pending component as `timed_out` / `aborted` and skips the iframe checks |
| `exclude` | `string[]` | `[]` | component names not to collect; they report `excluded` / `caller_excluded`; unknown names and non-strings are ignored |
| `binding` | `{ nonce: string; timestamp: number }` | none | anti-replay input from your server; see [Anti-replay binding](https://whiskerjs.com/docs/integration/#anti-replay-binding) |

## Types

### `CollectResult`

| Field | Type | Meaning |
| --- | --- | --- |
| `visitorId` | `string \| null` | 32 lowercase hex characters; `null` without a digest |
| `version` | `string` | library version, `'0.1.0'` |
| `signalsVersion` | `string` | `'9'` |
| `idVersion` | `string` | `'2'` |
| `components` | `Record<string, Component>` | all 47, always present |
| `structuredId` | `string \| null` | `'2_'` then seven 12-hex parts joined by `_`; `null` without a digest |
| `previousSignalsVersion` | `string \| null` | the signalsVersion the previous ids belong to, `'8'`; `null` when none are computed |
| `previousIdVersion` | `string \| null` | the idVersion the previous ids belong to, `'2'` |
| `previousVisitorId` | `string \| null` | the visitorId the previous version would give; `null` without a digest. See [Identifiers](https://whiskerjs.com/docs/identifiers/#previous-ids-linking-across-a-version-bump) |
| `previousStructuredId` | `string \| null` | the structuredId the previous version would give |
| `integrityFlags` | `string[]` | sorted names of the checks that fired |
| `durationMs` | `number` | whole milliseconds the collection took |
| `binding` | `Binding \| null` | `null` unless `binding` was passed |

The result is plain JSON: no functions, no `undefined` values, no cycles.

### `Component`

| Field | Type | Present |
| --- | --- | --- |
| `status` | `ComponentStatus` | always |
| `value` | `string` | only when `status` is `'ok'`; at most 128 printable ASCII characters |
| `hashed` | `boolean` | only when `status` is `'ok'`; `true` when `value` is a digest |
| `ruleId` | `string` | only when `status` is not `'ok'`; matches `^[a-z0-9_]{1,32}$` |

### `ComponentStatus`

`'ok' | 'unavailable' | 'blocked' | 'noisy' | 'excluded' | 'timed_out'`. Meanings and every ruleId: [How it works](https://whiskerjs.com/docs/how-it-works/#statuses-and-reason-codes).

### `Binding`

| Field | Type | Meaning |
| --- | --- | --- |
| `nonce` | `string \| null` | your nonce; `null` when the input was invalid |
| `timestamp` | `number \| null` | your timestamp; `null` when the input was invalid |
| `digest` | `string \| null` | 32 hex characters; `null` when invalid or without a digest |
| `ruleId` | `string` (optional) | why `digest` is `null`: `invalid_binding`, `invalid_nonce`, `invalid_timestamp` or `no_digest` |

### Other types

- `Agent`: `{ collect(options?): Promise<CollectResult> }`.
- `DigestLike`: `{ digest(algorithm: string, data: ArrayBuffer): Promise<ArrayBuffer> }`, the part of `SubtleCrypto` the library uses.
- `WindowLike`: `any`; collectors read the browser only through it.
- `BrowserClass`: `'brave' | 'chromium_webview' | 'firefox_fpp' | 'firefox_rfp' | 'rfp_hardened' | 'firefox' | 'safari_afp' | 'safari' | 'chromium' | 'other'`.

## Constants

| Export | Value |
| --- | --- |
| `VERSION` | `'0.1.0'` |
| `SIGNALS_VERSION` | `'9'` |
| `ID_VERSION` | `'2'` |
| `DEFAULT_TIMEOUT_MS` | `5000` |
| `COMPONENT_NAMES` | the 47 component names, sorted (frozen array) |
| `INTEGRITY_FLAG_NAMES` | the 19 flag names, sorted (frozen array) |
| `EXCLUSION_TABLE` | `Record<BrowserClass, Record<componentName, ruleId>>` (frozen) |
| `BROWSER_CLASSES` | the ten browser classes, in table order (frozen array) |
| `ID_GROUPS` | `Record<group, componentName[]>`, seven groups (frozen) |
| `ID_GROUP_ORDER` | `['gpu', 'audio', 'fonts', 'device', 'browser', 'locale', 'prefs']` |
| `ID_PART_LENGTH` | `12` (hex characters per structuredId part) |
| `PREVIOUS` | `{ signalsVersion: '8', idVersion: '2' }` or `null`: the versions of the previous ids (frozen) |

Not exported but fixed: the maximum `timeoutMs` (60000), the inner timeout per collector (1000 ms), the value length limit (128 characters).

## Testing with a fake environment

```ts
import { load } from 'whiskerjs';

const agent = await load({
  env: {
    window: myFakeWindow,          // anything Window-like
    now: () => fakeClock.now(),
    subtle: globalThis.crypto.subtle,
  },
});
const result = await agent.collect();
```

The project’s own tests do this with `test/fake-browser.ts`.
