> ## Documentation Index
> Fetch the complete documentation index at: https://kweri.uchenna.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Eviction & GC

> Configure garbage collection to keep memory usage bounded.

## Overview

Kweri's `EvictionEngine` periodically sweeps the cache and removes entries that are no longer needed. An entry is eligible for eviction when:

1. It has **no active observers** (no components are subscribed to it), AND
2. Its cache lifetime has expired (`now > updatedAt + cacheTime`) — or it was never populated (`updatedAt === 0`)

Entries that still have subscribers are never evicted, regardless of age.

## Automatic GC (default)

In the browser, GC runs **automatically** — you don't need to configure anything for `cacheTime` to be honored. The automatic sweeper is designed to cost nothing when idle:

* **Self-stopping** — it only runs a timer while there are entries to collect, and stops once the cache empties (re-arming on the next write).
* **Visibility-aware** — it pauses while the tab is hidden and resumes on focus.
* **SSR-safe** — on the server (no `document`) the automatic sweeper is **not** started. A server-side instance is request-scoped and collected wholesale, so no per-request timer leaks. (Still call `kweri.destroy()` when a request ends if you set an explicit `gcInterval`.)

```ts theme={null}
// Browser: cacheTime is honored automatically, no gcInterval needed.
const kweri = new Kweri({
  baseURL: 'https://api.example.com',
  cacheTime: 300_000, // unobserved entries collected ~5 minutes after going idle
})
```

## Explicit fixed-interval GC

Pass `gcInterval` to run a sweep on a fixed cadence instead of the automatic sweeper:

```ts theme={null}
const kweri = new Kweri({
  baseURL: 'https://api.example.com',
  cacheTime: 300_000,
  gcInterval: 60_000   // sweep every 60 seconds, always-on
})
```

## Manual control

```ts theme={null}
// Start GC after construction
kweri.startGC(60_000)

// Stop GC (e.g., during tests)
kweri.stopGC()
```

## isEligibleForEviction

The core eviction predicate is exported if you need to implement custom eviction logic:

```ts theme={null}
import { isEligibleForEviction } from 'kweri'

function isEligibleForEviction(
  entry: CacheEntry,
  observerCount: number,
  now?: number   // defaults to Date.now()
): boolean
```

An entry is eligible when:

```
observerCount === 0  AND  (entry.updatedAt === 0  OR  now > entry.updatedAt + entry.cacheTime)
```

## Custom timer (testing)

For unit tests, inject a `TimerAdapter` to control time:

```ts theme={null}
import { EvictionEngine, type TimerAdapter } from 'kweri'

const mockTimer: TimerAdapter = {
  setInterval: (fn, ms) => { /* store fn */ return 1 },
  clearInterval: () => {},
  setTimeout: (fn, ms) => { fn(); return 1 },
  clearTimeout: () => {},
  now: () => Date.now()
}

// Inject via the second Kweri constructor argument
const kweri = new Kweri({ baseURL: '...' }, mockTimer)
```

## Memory model

| Situation                           | Result                                                |
| ----------------------------------- | ----------------------------------------------------- |
| Component subscribed, data fresh    | Entry kept, no network request                        |
| Component subscribed, data stale    | Entry kept, background refetch                        |
| No subscribers, within cacheTime    | Entry kept in memory                                  |
| No subscribers, past cacheTime      | Entry eligible for next GC sweep                      |
| No subscribers, browser             | Collected automatically (auto-GC)                     |
| No subscribers, SSR / no `document` | Collected when the request-scoped instance is dropped |

<Tip>
  If your app navigates frequently between views, set a generous `cacheTime` (5–10 minutes). The automatic sweeper keeps recently-visited data warm for instant navigation while still reclaiming memory for old queries — no `gcInterval` required.
</Tip>
