# Devtools

> The development detectors, the timeline and the in-page panel of ./devtools; a stub in production.

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

In development, eight detectors report the mistakes that cost requests (duplicates, waterfalls, dead invalidations, refetch loops) and a panel shows the cache. 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).

## Setup

`src/lib/query.ts`

```ts
export const client = createClient({
	transport: http({ base: '/' }),
	modules: [
		devtools({
			thresholds: { duplicateMs: 2000, refetches: 20 }, // tighter or looser than the defaults
			ignore: [listTeams, 'waterfall'], // endpoints or detectors to stay silent about
			panel: true // Alt+Shift+Q opens and closes it
		})
	]
});
```

The `development` export condition resolves the real entry; every other build gets a stub of the same names (92 B gzip) with no detector, no panel and no message.

## Findings

Each finding is printed once per id with an `slq/<detector>` code. Read them in code, or assert them in tests with `t.findings()`:

`findings and timeline`

```ts
// What the detectors found so far, once per finding id; each carries an slq/<detector> code.
for (const finding of findings(client)) console.table(finding);

// The latest timeline records: requests, commits, invalidations, mutations (never a payload).
const last = timeline(client).slice(-20);
```

### Detectors

| Detector | Reports |
| --- | --- |
| `dead-invalidation` | An invalidation matched no entry and names a tag no endpoint provided. |
| `duplicate-request` | An entry sent again for mount or params right after a successful answer. |
| `waterfall` | A request started right after the entry its params or when read had committed. |
| `unscoped` | A scope: false endpoint whose data depends on the operator or region. |
| `access-in-when` | A right written as a when condition: it pauses but never removes forbidden data. |
| `leaked-optimistic` | An optimistic layer still applied long after its run succeeded. |
| `retry-storm` | Many retries to one origin, or one entry failing again and again. |
| `refetch-loop` | An entry requested again and again for causes other than poll and live. |

Detectors arm no timer: checks that wait for time to pass run on later events and when findings are read. Silence an endpoint or a detector with `ignore`.

### Thresholds

| Threshold | Default | Used by |
| --- | --- | --- |
| `duplicateMs` | `1000 ms` | duplicate-request |
| `waterfallMs` | `50 ms` | waterfall |
| `optimisticMs` | `10000 ms` | leaked-optimistic |
| `windowMs` | `10000 ms` | retry-storm, refetch-loop: sliding window |
| `retries` | `20` | retry-storm: retries to one origin |
| `failures` | `3` | retry-storm: failed sequences of one entry |
| `refetches` | `10` | refetch-loop: sends of one entry |

## Timeline

`timeline(client)` returns the latest records of a ring buffer (`buffer` records, never a payload, a params object or an error body): requests, commits, errors, removals, invalidations, observers and mutations. Requests and mutations also appear as Queue, Network, Commit, Mutations and Invalidate tracks in the browser's Performance panel (`timing`, on by default).

## Panel

`InspectButton.svelte`

```svelte
<!-- An application's own control for the devtools panel -->
<script lang="ts">
	import { getClient } from '@svelte-lean/query';
	import { openPanel } from '@svelte-lean/query/devtools';

	const client = getClient();
</script>

<button type="button" onclick={() => openPanel(client)}>Inspect queries</button>
```

- With `panel: true`, `Alt` + `Shift` + `Q` opens and closes it (`{ shortcut }` changes the keys, `false` removes them and leaves `openPanel(client)`); `Escape` closes it.
- It is a non-modal `<dialog>` in the top layer with five views: Entries (with a lazy view of the selected value), Tags, Causes, Timeline and Findings.
- Its UI is loaded on first use; closed, it costs one `keydown` listener.

## Reference

- [@svelte-lean/query/devtools](https://svelte-lean.vercel.app/query/api/devtools.md)
- [Testing](https://svelte-lean.vercel.app/query/testing.md)
