# @svelte-lean/query/devtools

> devtools(), findings(), timeline(), openPanel() and closePanel(): the development detectors, their thresholds, the timeline records and the panel.

Page: https://svelte-lean.vercel.app/query/api/devtools · Markdown: https://svelte-lean.vercel.app/query/api/devtools.md · Index: https://svelte-lean.vercel.app/llms.txt

devtools(): eight development detectors that report each finding once, a timeline ring buffer, Performance panel tracks and, with panel, an in-page panel. Production builds resolve the entry to a stub.

> **Note** `./devtools` is experimental: the API can still change in a minor release. Stability per entry point: [Entry points](https://svelte-lean.vercel.app/query.md#entry-points).

## Import

`import`

```ts
import { devtools, findings, timeline, openPanel, closePanel } from '@svelte-lean/query/devtools';
```

## Usage

`src/lib/api/modules.ts`

```ts
// src/lib/api/modules.ts — resolves to a stub in production builds.
import { devtools } from '@svelte-lean/query/devtools';

export const diagnostics = [
	devtools({
		thresholds: { duplicateMs: 2000 },
		ignore: ['waterfall'],
		panel: { shortcut: 'Alt+Shift+Q' } // opens and closes the in-page panel
	})
];
```

## Size

Production Vite builds of a small consumer entry, Svelte external. A marginal row is the bytes the entry adds over the row it names.

| Build | Over | gzip | brotli | Budget (gzip) |
| --- | --- | --- | --- | --- |
| `devtools (production stub)` | – | 92 B | 68 B | 100 B |

Read from `packages/query/artifacts/size.json`. Every entry and its method: [Performance](https://svelte-lean.vercel.app/query/performance.md).

## Functions

The module, two readers and the panel controls.

### Exports

Runtime exports of the entry; the production stub has the same names.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| # `devtools` | `(options?: DevtoolsOptions) => Module` | – | The detectors; does nothing in production. |
| # `findings` | `(client: Client) => Finding[]` | – | The client's findings so far. |
| # `timeline` | `(client: Client) => TimelineRecord[]` | – | The latest timeline records, oldest first. |
| # `detectors` | `DetectorName[]` | – | The detectors this build runs; empty in production. |
| # `openPanel` | `(client: Client) => Promise<() => void>` | – | Open the panel from an application control (loads its UI on first use); resolves to its close function. |
| # `closePanel` | `(client: Client) => Promise<void>` | – | Close the panel when it is open. |

## Options

DevtoolsOptions and the thresholds of the detectors that measure.

### DevtoolsOptions

All optional.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| # `buffer` | `number` | `2000` | Timeline records kept; 0: no timeline. |
| # `timing` | `'timestamp' \| 'measure' \| false` | `'timestamp'` | User Timing output of requests and mutations (Performance panel tracks). |
| # `report` | `(finding: Finding) => void` | `console.warn with slq/<detector>` | Where findings go. |
| # `ignore` | `(endpoint \| DetectorName)[]` | – | Endpoints or detectors to stay silent about. |
| # `thresholds` | `Partial<Thresholds>` | – | Detector limits; each one left out keeps its default. |
| # `panel` | `boolean \| { shortcut?: string \| false }` | `no panel` | The in-page panel: true for Alt+Shift+Q, or another shortcut; false as shortcut leaves only openPanel(). Closed, it costs one keydown listener. |

### Thresholds

Times are milliseconds.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| # `duplicateMs` | `number` | `1000` | duplicate-request: a remount this soon after a successful answer. |
| # `waterfallMs` | `number` | `50` | waterfall: a dependent request this soon after the commit it read. |
| # `optimisticMs` | `number` | `10000` | leaked-optimistic: a layer alive this long after its run succeeded. |
| # `windowMs` | `number` | `10000` | retry-storm, refetch-loop: the sliding window. |
| # `retries` | `number` | `20` | retry-storm: retry attempts to one origin within the window. |
| # `failures` | `number` | `3` | retry-storm: consecutive failed sequences of one entry. |
| # `refetches` | `number` | `10` | refetch-loop: sends of one entry within the window above this. |

## Records

What findings() and timeline() return. Never a payload, params object or error body.

### Finding

One report per id per client; codes are listed in docs/query/errors.md.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| # `id` | `string` | – | Detector, endpoint and tag. |
| # `detector` | `DetectorName` | – | The detector. |
| # `severity` | `'warn' \| 'info'` | – | Severity. |
| # `endpoint` | `string \| undefined` | – | The endpoint id. |
| # `message` | `string` | – | English, for developers. |
| # `data` | `Record<string, string \| number \| boolean>` | – | Measured values. |

### TimelineRecord

One record of the timeline.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| # `t` | `number` | – | Client clock, milliseconds. |
| # `event` | `TimelineEvent` | – | request, commit, error, remove, invalidate, observe, unobserve or mutation. |
| # `entry` | `number` | – | Entry number; 0 for invalidations and mutations. |
| # `endpoint` | `string \| undefined` | – | The endpoint id, or the invalidated targets. |
| # `detail` | `string \| undefined` | – | Cause, error kind, removal reason or outcome. |
| # `n` | `number` | – | Bytes of a commit, targets of an invalidation, else 0. |

### DetectorName

The detectors.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| # `dead-invalidation` | `DetectorName` | – | An invalidation that matches no entry and names a tag nothing provided. |
| # `duplicate-request` | `DetectorName` | – | An entry sent again for mount or params shortly after a successful answer. |
| # `waterfall` | `DetectorName` | – | A request started right after the commit of an entry its params or when read. |
| # `unscoped` | `DetectorName` | – | A scope: false endpoint with access, or whose answer changed with the scope. |
| # `access-in-when` | `DetectorName` | – | when caused a can() call: a right written as a state condition. |
| # `leaked-optimistic` | `DetectorName` | – | An optimistic layer alive long after its run succeeded. |
| # `retry-storm` | `DetectorName` | – | Many retries to one origin, or one entry failing again and again. |
| # `refetch-loop` | `DetectorName` | – | One entry requested many times in the window, for causes other than poll and live. |

## Guide

How to use it, with examples: [Devtools](https://svelte-lean.vercel.app/query/devtools.md). All entry points: [Svelte Lean Query](https://svelte-lean.vercel.app/query.md).
