O duxt emite **um** hook. É um hook de build do Nuxt, dispara uma vez por build
e entrega todas as páginas do site como registos de pesquisa planos.

```ts [modules/my-search.ts]
import { defineNuxtModule } from '@nuxt/kit';
import type { DuxtSearchRecord } from '@kirchdev/duxt/search-records';

export default defineNuxtModule({
  setup(_options, nuxt) {
    nuxt.hook('duxt:search:records', async (records) => {
      await upload(records);
    });
  }
});
```

Um exemplo completo — o esquema da coleção, o upsert, a remoção do que já não
existe e a chave que nunca pode chegar a uma página — está em
[Envie suas páginas para o Typesense](/add-ons/typesense).

## `duxt:search:records`

```ts
(records: readonly DuxtSearchRecord[]) => void | Promise<void>
```

**Quando dispara.** Depois de o Content ter analisado cada fonte, cada versão,
cada idioma e cada secção gerada — incluindo os repositórios remotos que
descarregou — e **antes** de o Nitro prerenderizar ou empacotar o que quer que
seja. Um listener ainda pode escrever um ficheiro em `public/` e vê-lo chegar à
saída.

**O que é um registo.** Uma página, mais um por cada cabeçalho que ela tenha.

| Campo     | Tipo     | O que contém                                                                |
| :-------- | :------- | :-------------------------------------------------------------------------- |
| `id`      | `string` | A identidade do registo, e uma chave primária válida em qualquer motor      |
| `url`     | `string` | Onde o leitor aterra — segmento de idioma e âncora incluídos                  |
| `title`   | `string` | O cabeçalho que o registo cobre, ou o título da página no primeiro          |
| `content` | `string` | As palavras sob esse cabeçalho, sem marcação e com espaços normalizados     |
| `source`  | `string` | De que fonte veio, como segmentos de URL sem a versão                       |
| `version` | `string` | O rótulo da versão, onde a fonte tem versões — ausente onde não tem           |
| `locale`  | `string` | O idioma em que o **conteúdo** está escrito                                 |

`id` é um resumo hexadecimal em vez do URL, porque o Meilisearch só aceita
`A-Za-z0-9_-` como identificador de documento. Deriva de `url`, por isso é igual
em qualquer máquina e mantém-se enquanto a página ficar onde está — que é
exatamente o que permite a um fornecedor fazer upsert em vez de reconstruir.

`source` é uma **identidade, não um endereço**: `/` para a documentação servida
na raiz, `/harbour` para um segundo repositório, `/harbour/api` para uma secção
gerada lá dentro. A versão fica deliberadamente de fora, para que uma faceta
construída sobre `source` sobreviva a um lançamento e `version`, ao lado, seja o
que restringe a uma só.

`locale` é o idioma do conteúdo, não o idioma de interface em que se está a ler.
Um site com fontes não traduzidas obtém **um** conjunto de registos mesmo
servindo sete idiomas — o mesmo parágrafo indexado sete vezes é uma resposta
repetida.

::callout{type="warning" title="A carga está congelada"}
Constrói ou envia o teu próprio índice a partir dela. Não reescrevas um registo
nem ordenes o array no lugar: todos os outros listeners recebem o mesmo objeto,
e uma edição de passagem seria herdada pelo seguinte sem nada o indicar.
::

### O que ele deliberadamente não leva

- **Nenhum excerto.** Um excerto é uma decisão sobre uma lista de resultados, e
  a lista de resultados é tua.
- **Nenhum nome de coleção.** Isso é a contabilidade interna do índice do duxt,
  e um documento com essa chave parte no dia em que uma fonte ganha um idioma.
- **Nenhum rótulo de exibição.** `label`, `name` e a legenda da versão são prosa
  para quem lê, traduzida por idioma. Um registo não é lido por ninguém.

### O que fica fora do índice

Uma página que escreveu [`search: false`](/reference/frontmatter) não produz
registo nenhum — a mesma exclusão que a caixa de pesquisa, a ferramenta MCP
`search_docs` e ambos os índices llms já respeitam. O mesmo vale para uma fonte
traduzida num idioma que `duxt.locales` não serve, porque indexá-la seria
oferecer ao leitor um URL que devolve 404.

Onde um site serve **duas regiões de um mesmo idioma** sobre uma só árvore —
`pt-PT` e `pt-BR` a ler a mesma pasta `pt/` —, os registos endereçam a primeira.
Os dois URL levam as mesmas palavras, por isso indexar ambos duplicaria o índice
para dizer duas vezes o mesmo; um fornecedor que queira levar o leitor à sua
própria região troca o segmento, o que o duxt não pode fazer porque não sabe
quem está a pesquisar.

E quando ninguém está a ouvir, o duxt não constrói carga nenhuma: um site sem
fornecedor não paga nada por o hook existir.

## Por que existe apenas um

Uma extensão distribuível do duxt é **uma camada Nuxt**, não um objeto plugin:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  extends: ['@kirchdev/duxt', 'duxt-typesense']
});
```

Isso já leva componentes, predefinições de `app.config`, coleções de
`content.config.ts`, rotas de servidor, ferramentas MCP e um módulo próprio,
num pacote com versão própria. Uma API `duxt.plugins` reimplementaria a
resolução de camadas dentro de uma camada — e um registo onde um plugin escreve
no momento do import bate na mesma parede que moldou
[`sectionTypes`](/reference/configuration): `content.config.ts` e o módulo do
duxt são carregados por **carregadores diferentes**, por isso um registo mutável
onde ambos escrevessem seriam dois registos. Um hook de build do Nuxt não tem
esse problema, porque só a metade do módulo chega a vê-lo.

Por isso a superfície mantém-se pequena de propósito, e cada uma das outras
entradas continua a ser a resposta certa à sua própria pergunta:

| Queres mudar                             | Usa                                                               |
| :--------------------------------------- | :------------------------------------------------------------------ |
| Uma cor, um tipo de letra, um espaçamento | Um token CSS — [Sobrepor o tema](/guides/override-the-theme)        |
| O que um componente desenha              | Sombreá-lo, ou um slot onde exista                                  |
| O que o site diz sobre si próprio        | A chave `duxt` de `app.config` — [Configuração](/reference/configuration) |
| Um novo tipo de secção gerada            | `sectionTypes` — [Fontes](/reference/sources)                        |
| O que uma página desenha à sua volta     | Frontmatter — [Frontmatter](/reference/frontmatter)                  |
| Onde o texto do site é indexado          | Este hook                                                           |

::callout{type="tip" title="Pedir um segundo hook"}
O nome de um hook é superfície pública: renomeá-lo é uma alteração
incompatível, e um que não se usa já não se consegue remover. Por isso um novo
chega quando existe um consumidor que não se consegue escrever sem ele — não por
antecipação, e não porque uma costura pareça vir a dar jeito um dia.
::