# API

> The composable, the hooks, and the page and link attributes.

## `usePrecog()`

```ts
const { enabled, pause, resume, refresh, grantConsent, metrics, plan } = usePrecog();
```

<table>
<thead>
  <tr>
    <th>
      Member
    </th>
    
    <th>
      What it does
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        enabled
      </code>
    </td>
    
    <td>
      False while paused, or before consent when <code>
        privacy.requireConsent
      </code>
      
       is on.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pause()
      </code>
    </td>
    
    <td>
      Stops predicting and removes the speculation rules.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        resume()
      </code>
    </td>
    
    <td>
      Starts again.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        refresh()
      </code>
    </td>
    
    <td>
      Predicts now, ignoring <code>
        minIntervalMs
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        grantConsent()
      </code>
    </td>
    
    <td>
      Lets the module run when <code>
        privacy.requireConsent
      </code>
      
       is on.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        metrics
      </code>
    </td>
    
    <td>
      Counters, latency percentiles, hit rate and the estimated cost.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        plan
      </code>
    </td>
    
    <td>
      What is speculated right now, or <code>
        null
      </code>
      
      .
    </td>
  </tr>
</tbody>
</table>

<note>

`pause()` stays paused until you call `resume()`. A navigation will not undo it, which is a
deliberate change from an earlier version that silently resumed on every route change.

</note>

On the server, and when the module is off, `usePrecog()` returns a no-op object, so a page can
call it unconditionally.

## Per page

```vue
<script setup>
definePageMeta({ precog: false });
</script>
```

The module collects nothing and speculates nothing while this page is open, and picks up again
when the visitor leaves it.

## Per link

```vue
<template>
  <NuxtLink to="/settings" data-precog="off">Never speculate this</NuxtLink>
  <NuxtLink to="/pricing" data-precog="hint">Always a candidate, even off screen</NuxtLink>
</template>
```

## Client hooks

```ts
nuxtApp.hook("precog:decision", (plan, trigger) => {
  // trigger is 'route' | 'scroll' | 'pointer' | 'dom' | 'manual' | 'visible'
});

nuxtApp.hook("precog:metrics", (summary) => {
  // summary.hitRate, summary.p95, summary.inputTokens, ...
});
```

A route change fires `precog:decision` with an empty plan, because the links the old plan named
are gone.

## Server hooks

In a Nitro plugin:

```ts [server/plugins/precog.ts]
export default defineNitroPlugin((nitro) => {
  nitro.hooks.hook("precog:predict", (ctx) => {
    // Read ctx.state, or set ctx.prediction to answer without calling Jev at all.
  });

  nitro.hooks.hook("precog:predicted", (ctx) => {
    // Every fresh answer. ctx.substituted is true when it came from the hook above.
  });
});
```

`precog:predict` is how the playground runs with no API key, and how you would swap in your own
model. `precog:predicted` is how it records real answers for replay.

<note>

A `precog:predicted` listener that throws is ignored: the prediction already happened and the
visitor gets it. A `precog:predict` listener that throws fails the request, because that hook is
allowed to replace the answer.

</note>

When a prediction fails, the endpoint answers `503` with an empty prediction and puts a short
reason in the `x-precog-reason` header. The client falls open either way.

## Types

```ts
import type {
  PrecogPlan,
  PrecogPrediction,
  PrecogState,
  PrecogCandidate,
  MetricsSummary,
} from "nuxt-precog";
```
